2
0
mirror of https://github.com/iconify/iconify.git synced 2024-11-09 23:00:56 +00:00

Change Vue 2 component from functional to class in preparation for API support

This commit is contained in:
Vjacheslav Trushkin 2021-05-07 00:28:14 +03:00
parent a4422ec145
commit 56df8fa1e8
28 changed files with 1986 additions and 976 deletions

View File

@ -1,6 +1,6 @@
{
"$schema": "https://developer.microsoft.com/json-schemas/api-extractor/v7/api-extractor.schema.json",
"mainEntryPointFilePath": "lib/IconifyIcon.d.ts",
"mainEntryPointFilePath": "lib/iconify.d.ts",
"bundledPackages": [
"@iconify/types",
"@iconify/core",
@ -15,7 +15,7 @@
},
"dtsRollup": {
"enabled": true,
"untrimmedFilePath": "<projectFolder>/dist/IconifyIcon.d.ts"
"untrimmedFilePath": "<projectFolder>/dist/iconify.d.ts"
},
"tsdocMetadata": {
"enabled": false

View File

@ -0,0 +1,43 @@
{
"$schema": "https://developer.microsoft.com/json-schemas/api-extractor/v7/api-extractor.schema.json",
"mainEntryPointFilePath": "lib/offline.d.ts",
"bundledPackages": [
"@iconify/types",
"@iconify/core",
"@cyberalien/redundancy"
],
"compiler": {},
"apiReport": {
"enabled": false
},
"docModel": {
"enabled": false
},
"dtsRollup": {
"enabled": true,
"untrimmedFilePath": "<projectFolder>/dist/offline.d.ts"
},
"tsdocMetadata": {
"enabled": false
},
"messages": {
"compilerMessageReporting": {
"default": {
"logLevel": "warning"
}
},
"extractorMessageReporting": {
"default": {
"logLevel": "warning"
},
"ae-missing-release-tag": {
"logLevel": "none"
}
},
"tsdocMessageReporting": {
"default": {
"logLevel": "warning"
}
}
}
}

View File

@ -2,21 +2,18 @@ const fs = require('fs');
const path = require('path');
const child_process = require('child_process');
// const packagesDir = path.dirname(path.dirname(__dirname)) + '/packages';
const packagesDir = path.dirname(__dirname);
// List of commands to run
const commands = [];
// Parse command line
const compile = {
// core: false,
core: false,
lib: true,
dist: true,
api: true,
};
// Disable compiling dependencies because project was moved to archive
/*
process.argv.slice(2).forEach((cmd) => {
if (cmd.slice(0, 2) !== '--') {
return;
@ -79,7 +76,6 @@ if (compile.core) {
cwd: packagesDir + '/core',
});
}
*/
// Compile other packages
Object.keys(compile).forEach((key) => {

View File

@ -6,4 +6,5 @@ module.exports = {
'^.+\\.js$': '<rootDir>/node_modules/babel-jest',
},
collectCoverage: false,
testMatch: ['**/tests/**/*.test.js'],
};

View File

@ -15,13 +15,14 @@
"build": "node build",
"build:lib": "tsc -b",
"build:dist": "rollup -c rollup.config.js",
"build:api": "api-extractor run --local --verbose",
"prebuild:api": "api-extractor run --local --verbose --config api-extractor.offline.json",
"build:api": "api-extractor run --local --verbose --config api-extractor.iconify.json",
"pretest": "npm run build",
"test": "jest"
},
"main": "dist/IconifyIcon.umd.js",
"module": "dist/IconifyIcon.esm.js",
"types": "dist/IconifyIcon.d.ts",
"main": "dist/iconify.js",
"module": "dist/iconify.mjs",
"types": "dist/iconify.d.ts",
"devDependencies": {
"@iconify/core": "^1.0.0-rc.3",
"@microsoft/api-extractor": "^7.15.1",

View File

@ -1,78 +1,45 @@
import resolve from '@rollup/plugin-node-resolve';
import commonjs from '@rollup/plugin-commonjs';
import buble from '@rollup/plugin-buble';
// import { terser } from 'rollup-plugin-terser';
const name = 'IconifyIcon';
const names = ['offline', 'iconify'];
const component = 'Icon';
// Module footer
const footer = `
// Export as ES module
if (typeof exports === 'object') {
try {
exports.__esModule = true;
exports.default = ${name};
} catch (err) {
}
}`;
const config = [];
// Export configuration
const config = [
// ES Module
{
// Write all packages
names.forEach((name) => {
// ES module
config.push({
input: `lib/${name}.js`,
output: [
{
file: `dist/${name}.esm.js`,
file: `dist/${name}.mjs`,
format: 'esm',
exports: 'named',
},
],
external: ['vue'],
plugins: [resolve(), commonjs(), buble()],
},
// UMD Module
{
input: `lib/${name}.js`,
output: [
{
file: `dist/${name}.umd.js`,
format: 'umd',
name,
exports: 'named',
},
],
plugins: [resolve(), commonjs(), buble()],
},
/*
// Web and module
// "unpkg": "dist/IconifyIcon.min.js",
});
{
// UMD module
config.push({
input: `lib/${name}.js`,
output: [
{
file: `dist/${name}.js`,
name,
format: 'iife',
footer,
},
],
plugins: [resolve(), commonjs(), buble()],
},
// Web
{
input: `lib/${name}.js`,
output: [
{
file: `dist/${name}.min.js`,
name,
format: 'umd',
name: component,
exports: 'named',
format: 'iife',
globals: {
vue: 'Vue',
},
},
],
plugins: [resolve(), commonjs(), buble(), terser()],
},
*/
];
external: ['vue'],
plugins: [resolve(), commonjs(), buble()],
});
});
export default config;

View File

@ -1,339 +0,0 @@
import _Vue, { PluginFunction, VueConstructor, VNode, VNodeData } from 'vue';
import { FunctionalRenderContext } from 'vue/src/core';
import { IconifyIcon as IconifyIconData, IconifyJSON } from '@iconify/types';
import {
IconifyIconCustomisations as IconCustomisations,
FullIconCustomisations,
defaults,
IconifyHorizontalIconAlignment,
IconifyVerticalIconAlignment,
IconifyIconSize,
} from '@iconify/core/lib/customisations';
import {
flipFromString,
alignmentFromString,
} from '@iconify/core/lib/customisations/shorthand';
import { rotateFromString } from '@iconify/core/lib/customisations/rotate';
import { fullIcon } from '@iconify/core/lib/icon';
import { iconToSVG } from '@iconify/core/lib/builder';
import { replaceIDs } from '@iconify/core/lib/builder/ids';
import { merge } from '@iconify/core/lib/misc/merge';
import { parseIconSet } from '@iconify/core/lib/icon/icon-set';
/**
* Export types that could be used in component
*/
export {
IconifyIconData,
IconifyJSON,
IconifyHorizontalIconAlignment,
IconifyVerticalIconAlignment,
IconifyIconSize,
};
// Allow rotation to be string
/**
* Icon customisations
*/
export type IconifyIconCustomisations = IconCustomisations & {
rotate?: string | number;
};
/**
* Icon properties
*/
export interface IconifyIconProps extends IconifyIconCustomisations {
icon: IconifyIconData;
// Style
color?: string;
// Shorthand properties
flip?: string;
align?: string;
// Aliases for alignment because "v-align" is treated like directive
horizontalAlign?: IconifyHorizontalIconAlignment;
verticalAlign?: IconifyVerticalIconAlignment;
// Aliases for flip because "v-flip" is treated like directive
horizontalFlip?: boolean;
verticalFlip?: boolean;
}
// Interface for functional component context that is missing in Vue types.
// Missing some unused stuff: children, slots, scopedSlots, injections
// interface FunctionalRenderContext {
// props: { [key: string]: unknown };
// data?: VNodeData;
// parent?: VNode;
// listeners?: object; // alias of data.on
// }
/**
* Default SVG attributes
*/
const svgDefaults = {
'xmlns': 'http://www.w3.org/2000/svg',
'xmlns:xlink': 'http://www.w3.org/1999/xlink',
'aria-hidden': true,
'role': 'img',
};
/**
* Aliases for customisations.
* In Vue 'v-' properties are reserved, so v-align and v-flip must be renamed
*/
const customisationAliases = {
horizontalAlign: 'hAlign',
verticalAlign: 'vAlign',
horizontalFlip: 'hFlip',
verticalFlip: 'vFlip',
};
/**
* Storage for icons referred by name
*/
const storage: Record<string, Required<IconifyIconData>> = Object.create(null);
/**
* Interface for style variable
*/
type VNodeStyle = (string | Record<string, unknown>)[];
/**
* IconifyIcon component
*/
const IconifyIcon = {
name: 'IconifyIcon',
functional: true,
/**
* Render icon
*
* @param createElement
* @param context
*/
render(
createElement: typeof _Vue.prototype.$createElement,
context: FunctionalRenderContext
): VNode {
const props = context.props;
// Split properties
const icon =
typeof props.icon === 'string'
? storage[props.icon]
: fullIcon(props.icon);
if (!icon) {
return null;
}
const customisations = merge(
defaults,
props as IconifyIconCustomisations
) as FullIconCustomisations;
const componentProps = merge(svgDefaults);
// Copy style
let stylesList: VNodeStyle;
let styleString: string;
let isStyleString = false;
let hasStyle = true;
function setStyle(value: unknown): boolean {
if (typeof value === 'string') {
// Style as string
styleString = value;
isStyleString = true;
return true;
}
if (typeof value !== 'object') {
// Unknown type ???
return false;
}
stylesList = value instanceof Array ? value.slice(0) : [value];
return true;
}
const contextData = context.data;
if (
!contextData ||
(!setStyle(contextData.staticStyle) && !setStyle(contextData.style))
) {
stylesList = [];
hasStyle = false;
}
// Get element properties
for (let key in props) {
const value = props[key];
switch (key) {
// Properties to ignore
case 'icon':
case 'style':
break;
// Flip as string: 'horizontal,vertical'
case 'flip':
flipFromString(customisations, value);
break;
// Alignment as string
case 'align':
alignmentFromString(customisations, value);
break;
// Color: copy to style
case 'color':
if (isStyleString) {
styleString = 'color: ' + value + '; ' + styleString;
} else {
stylesList.unshift({
color: value,
});
}
hasStyle = true;
break;
// Rotation as string
case 'rotate':
if (typeof value !== 'number') {
customisations[key] = rotateFromString(value);
} else {
componentProps[key] = value;
}
break;
// Remove aria-hidden
case 'ariaHidden':
case 'aria-hidden':
// Vue transforms 'aria-hidden' property to 'ariaHidden'
if (value !== true && value !== 'true') {
delete componentProps['aria-hidden'];
}
break;
default:
if (customisationAliases[key] !== void 0) {
// Aliases for customisations
customisations[customisationAliases[key]] = value;
} else if (defaults[key] === void 0) {
// Copy missing property if it does not exist in customisations
componentProps[key] = value;
}
}
}
// Generate icon
const item = iconToSVG(icon, customisations);
// Add icon stuff
for (let key in item.attributes) {
componentProps[key] = item.attributes[key];
}
if (item.inline) {
if (isStyleString) {
styleString = 'vertical-align: -0.125em; ' + styleString;
} else {
stylesList.unshift({
verticalAlign: '-0.125em',
});
}
hasStyle = true;
}
// Generate node data
const data: VNodeData = {
attrs: componentProps,
domProps: {
innerHTML: replaceIDs(item.body),
},
};
if (hasStyle) {
data.style = isStyleString ? styleString : stylesList;
}
if (contextData) {
['on', 'ref'].forEach((attr) => {
if (contextData[attr] !== void 0) {
data[attr] = contextData[attr];
}
});
['staticClass', 'class'].forEach((attr) => {
if (contextData[attr] !== void 0) {
data.class = contextData[attr];
}
});
}
return createElement('svg', data);
},
/**
* Add icon to storage that can later be used by name, for example: <iconify-icon icon="home" />
*/
addIcon: (name: string, data: IconifyIconData) => {
storage[name] = fullIcon(data);
},
/**
* Add collection to storage, allowing to call icons by name
*
* @param data Icon set
* @param prefix Optional prefix to add to icon names, true if prefix from icon set should be used.
*/
addCollection: (data: IconifyJSON, prefix?: string | boolean) => {
const iconPrefix: string =
typeof prefix === 'string'
? prefix
: prefix !== false && typeof data.prefix === 'string'
? data.prefix + ':'
: '';
parseIconSet(data, (name, icon) => {
if (icon !== null) {
storage[iconPrefix + name] = icon;
}
});
},
};
// Install function
interface InstallFunction extends PluginFunction<unknown> {
installed?: boolean;
}
interface InstallableComponent extends VueConstructor<_Vue> {
install: InstallFunction;
}
const install: InstallFunction = function installIconifyIcon(Vue: typeof _Vue) {
if (install.installed) return;
install.installed = true;
Vue.component('IconifyIcon', IconifyIcon);
};
// Create module definition for Vue.use()
const plugin = {
install,
};
// Auto-install when vue is found (eg. in browser via <script> tag)
let GlobalVue = null;
if (typeof window !== 'undefined') {
GlobalVue = window.Vue;
} else if (typeof global !== 'undefined') {
GlobalVue = ((global as unknown) as Record<string, unknown>).Vue;
}
if (GlobalVue) {
GlobalVue.use(plugin);
}
// Inject install function into component - allows component
// to be registered via Vue.use() as well as Vue.component()
((IconifyIcon as unknown) as InstallableComponent).install = install;
// Export component
export default IconifyIcon;

View File

@ -0,0 +1,114 @@
import Vue, { CreateElement, VNode } from 'vue';
import { ExtendedVue } from 'vue/types/vue';
import { IconifyIcon, IconifyJSON } from '@iconify/types';
import {
IconifyHorizontalIconAlignment,
IconifyVerticalIconAlignment,
IconifyIconSize,
} from '@iconify/core/lib/customisations';
import { fullIcon } from '@iconify/core/lib/icon';
import { parseIconSet } from '@iconify/core/lib/icon/icon-set';
import {
IconifyIconCustomisations,
IconifyIconProps,
IconProps,
} from './props';
import { render } from './render';
/**
* Export stuff from props.ts
*/
export { IconifyIconCustomisations, IconifyIconProps, IconProps };
/**
* Export types that could be used in component
*/
export {
IconifyIcon,
IconifyJSON,
IconifyHorizontalIconAlignment,
IconifyVerticalIconAlignment,
IconifyIconSize,
};
/**
* Storage for icons referred by name
*/
const storage: Record<string, Required<IconifyIcon>> = Object.create(null);
/**
* Add icon to storage, allowing to call it by name
*
* @param name
* @param data
*/
export function addIcon(name: string, data: IconifyIcon): void {
storage[name] = fullIcon(data);
}
/**
* Add collection to storage, allowing to call icons by name
*
* @param data Icon set
* @param prefix Optional prefix to add to icon names, true (default) if prefix from icon set should be used.
*/
export function addCollection(
data: IconifyJSON,
prefix?: string | boolean
): void {
const iconPrefix: string =
typeof prefix === 'string'
? prefix
: prefix !== false && typeof data.prefix === 'string'
? data.prefix + ':'
: '';
parseIconSet(data, (name, icon) => {
if (icon !== null) {
storage[iconPrefix + name] = icon;
}
});
}
/**
* Component
*/
export const Icon = Vue.extend({
// Do not inherit other attributes: it is handled by render()
// In Vue 2 style is still passed!
inheritAttrs: false,
// Render icon
render(createElement: CreateElement): VNode {
const props = this.$attrs;
// Check icon
const icon =
typeof props.icon === 'string'
? storage[props.icon]
: typeof props.icon === 'object'
? fullIcon(props.icon)
: null;
// Validate icon object
if (
icon === null ||
typeof icon !== 'object' ||
typeof icon.body !== 'string'
) {
// Render child nodes
if (this.$slots.default) {
const result = this.$slots.default;
if (result instanceof Array && result.length > 0) {
// If there are multiple child nodes, they must be wrapped in Vue 2
return result.length === 1
? result[0]
: createElement('span', result);
}
}
return (null as unknown) as VNode;
}
// Valid icon: render it
return render(createElement, props, this.$data, icon);
},
});

View File

@ -0,0 +1,114 @@
import Vue, { CreateElement, VNode } from 'vue';
import { ExtendedVue } from 'vue/types/vue';
import { IconifyIcon, IconifyJSON } from '@iconify/types';
import {
IconifyHorizontalIconAlignment,
IconifyVerticalIconAlignment,
IconifyIconSize,
} from '@iconify/core/lib/customisations';
import { fullIcon } from '@iconify/core/lib/icon';
import { parseIconSet } from '@iconify/core/lib/icon/icon-set';
import {
IconifyIconCustomisations,
IconifyIconProps,
IconProps,
} from './props';
import { render } from './render';
/**
* Export stuff from props.ts
*/
export { IconifyIconCustomisations, IconifyIconProps, IconProps };
/**
* Export types that could be used in component
*/
export {
IconifyIcon,
IconifyJSON,
IconifyHorizontalIconAlignment,
IconifyVerticalIconAlignment,
IconifyIconSize,
};
/**
* Storage for icons referred by name
*/
const storage: Record<string, Required<IconifyIcon>> = Object.create(null);
/**
* Add icon to storage, allowing to call it by name
*
* @param name
* @param data
*/
export function addIcon(name: string, data: IconifyIcon): void {
storage[name] = fullIcon(data);
}
/**
* Add collection to storage, allowing to call icons by name
*
* @param data Icon set
* @param prefix Optional prefix to add to icon names, true (default) if prefix from icon set should be used.
*/
export function addCollection(
data: IconifyJSON,
prefix?: string | boolean
): void {
const iconPrefix: string =
typeof prefix === 'string'
? prefix
: prefix !== false && typeof data.prefix === 'string'
? data.prefix + ':'
: '';
parseIconSet(data, (name, icon) => {
if (icon !== null) {
storage[iconPrefix + name] = icon;
}
});
}
/**
* Component
*/
export const Icon = Vue.extend({
// Do not inherit other attributes: it is handled by render()
// In Vue 2 style is still passed!
inheritAttrs: false,
// Render icon
render(createElement: CreateElement): VNode {
const props = this.$attrs;
// Check icon
const icon =
typeof props.icon === 'string'
? storage[props.icon]
: typeof props.icon === 'object'
? fullIcon(props.icon)
: null;
// Validate icon object
if (
icon === null ||
typeof icon !== 'object' ||
typeof icon.body !== 'string'
) {
// Render child nodes
if (this.$slots.default) {
const result = this.$slots.default;
if (result instanceof Array && result.length > 0) {
// If there are multiple child nodes, they must be wrapped in Vue 2
return result.length === 1
? result[0]
: createElement('span', result);
}
}
return (null as unknown) as VNode;
}
// Valid icon: render it
return render(createElement, props, this.$data, icon);
},
});

View File

@ -0,0 +1,41 @@
import { IconifyIcon } from '@iconify/types';
import { IconifyIconCustomisations as IconCustomisations } from '@iconify/core/lib/customisations';
// Allow rotation to be string
/**
* Icon customisations
*/
export type IconifyIconCustomisations = IconCustomisations & {
rotate?: string | number;
};
/**
* Icon properties
*/
export interface IconifyIconProps extends IconifyIconCustomisations {
// Icon object
icon: IconifyIcon | string;
// Style
color?: string;
// Shorthand properties
flip?: string;
align?: string;
}
/**
* Properties for element that are mentioned in render.ts
*/
interface IconifyElementProps {
// Unique id, used as base for ids for shapes. Use it to get consistent ids for server side rendering
id?: string;
// Style
style?: unknown;
}
/**
* Mix of icon properties and HTMLElement properties
*/
export type IconProps = IconifyElementProps & IconifyIconProps;

178
packages/vue2/src/render.ts Normal file
View File

@ -0,0 +1,178 @@
import _Vue, { VNode, VNodeData, RenderContext } from 'vue';
import { IconifyIcon } from '@iconify/types';
import {
FullIconCustomisations,
defaults,
} from '@iconify/core/lib/customisations';
import {
flipFromString,
alignmentFromString,
} from '@iconify/core/lib/customisations/shorthand';
import { rotateFromString } from '@iconify/core/lib/customisations/rotate';
import { iconToSVG } from '@iconify/core/lib/builder';
import { replaceIDs } from '@iconify/core/lib/builder/ids';
import { merge } from '@iconify/core/lib/misc/merge';
import { IconifyIconCustomisations, IconProps } from './props';
/**
* Default SVG attributes
*/
const svgDefaults: Record<string, unknown> = {
'xmlns': 'http://www.w3.org/2000/svg',
'xmlns:xlink': 'http://www.w3.org/1999/xlink',
'aria-hidden': true,
'role': 'img',
};
/**
* Aliases for customisations.
* In Vue 'v-' properties are reserved, so v-align and v-flip must be renamed
*/
let customisationAliases = {};
['horizontal', 'vertical'].forEach((prefix) => {
['Align', 'Flip'].forEach((suffix) => {
const attr = prefix.slice(0, 1) + suffix;
// vertical-align
customisationAliases[prefix + '-' + suffix.toLowerCase()] = attr;
// v-align
customisationAliases[
prefix.slice(0, 1) + '-' + suffix.toLowerCase()
] = attr;
// verticalAlign
customisationAliases[prefix + suffix] = attr;
});
});
/**
* Interface for inline style
*/
type VNodeStyle = (string | Record<string, unknown>)[];
/**
* Render icon
*/
export const render = (
createElement: typeof _Vue.prototype.$createElement,
// context.props
props: IconProps,
// context.data
contextData: VNodeData,
// Icon must be validated before calling this function
icon: Required<IconifyIcon>
): VNode => {
// Split properties
const customisations = merge(
defaults,
props as IconifyIconCustomisations
) as FullIconCustomisations;
const componentProps = merge(svgDefaults);
// Style in Vue 2 components is always passed to rendered component, so no point in parsing it
const style: Record<string, unknown> = {};
// Get element properties
for (let key in props) {
const value = props[key];
switch (key) {
// Properties to ignore
case 'icon':
case 'style':
break;
// Flip as string: 'horizontal,vertical'
case 'flip':
if (typeof value === 'string') {
flipFromString(customisations, value);
}
break;
// Alignment as string
case 'align':
if (typeof value === 'string') {
alignmentFromString(customisations, value);
}
break;
// Color: override style
case 'color':
style.color = value;
break;
// Rotation as string
case 'rotate':
if (typeof value === 'string') {
customisations[key] = rotateFromString(value);
} else if (typeof value === 'number') {
customisations[key] = value;
}
break;
// Remove aria-hidden
case 'ariaHidden':
case 'aria-hidden':
// Vue transforms 'aria-hidden' property to 'ariaHidden'
if (value !== true && value !== 'true') {
delete componentProps['aria-hidden'];
}
break;
default:
if (customisationAliases[key] !== void 0) {
// Aliases for customisations
customisations[customisationAliases[key]] = value;
} else if (defaults[key] === void 0) {
// Copy missing property if it does not exist in customisations
componentProps[key] = value;
}
}
}
// Generate icon
const item = iconToSVG(icon, customisations);
// Add icon stuff
for (let key in item.attributes) {
componentProps[key] = item.attributes[key];
}
if (item.inline) {
style.verticalAlign = '-0.125em';
}
// Counter for ids based on "id" property to render icons consistently on server and client
let localCounter = 0;
const id = props.id;
// Generate node data
const data: VNodeData = {
attrs: componentProps,
domProps: {
innerHTML: replaceIDs(
item.body,
id ? () => id + '-' + localCounter++ : 'iconify-vue-'
),
},
};
if (Object.keys(style).length > 0) {
data.style = style;
}
if (contextData) {
['on', 'ref'].forEach((attr) => {
if (contextData[attr] !== void 0) {
data[attr] = contextData[attr];
}
});
['staticClass', 'class'].forEach((attr) => {
if (contextData[attr] !== void 0) {
data.class = contextData[attr];
}
});
}
// Render icon
return createElement('svg', data);
};

View File

@ -1,570 +0,0 @@
import { mount } from '@vue/test-utils';
import IconifyIcon from '../';
const iconData = {
body:
'<path d="M4 19h16v2H4zm5-4h11v2H9zm-5-4h16v2H4zm0-8h16v2H4zm5 4h11v2H9z" fill="currentColor"/>',
width: 24,
height: 24,
};
const iconDataWithID = {
body:
'<defs><path id="ssvg-id-1st-place-medala" d="M.93.01h120.55v58.36H.93z"/><path id="ssvg-id-1st-place-medald" d="M.93.01h120.55v58.36H.93z"/><path id="ssvg-id-1st-place-medalf" d="M.93.01h120.55v58.36H.93z"/><path id="ssvg-id-1st-place-medalh" d="M.93.01h120.55v58.36H.93z"/><path id="ssvg-id-1st-place-medalj" d="M.93.01h120.55v58.36H.93z"/><path id="ssvg-id-1st-place-medalm" d="M.93.01h120.55v58.36H.93z"/><path d="M52.849 78.373v-3.908c3.681-.359 6.25-.958 7.703-1.798c1.454-.84 2.54-2.828 3.257-5.962h4.021v40.385h-5.437V78.373h-9.544z" id="ssvg-id-1st-place-medalp"/><linearGradient x1="49.998%" y1="-13.249%" x2="49.998%" y2="90.002%" id="ssvg-id-1st-place-medalb"><stop stop-color="#1E88E5" offset="13.55%"/><stop stop-color="#1565C0" offset="93.8%"/></linearGradient><linearGradient x1="26.648%" y1="2.735%" x2="77.654%" y2="105.978%" id="ssvg-id-1st-place-medalk"><stop stop-color="#64B5F6" offset="13.55%"/><stop stop-color="#2196F3" offset="94.62%"/></linearGradient><radialGradient cx="22.368%" cy="12.5%" fx="22.368%" fy="12.5%" r="95.496%" id="ssvg-id-1st-place-medalo"><stop stop-color="#FFEB3B" offset="29.72%"/><stop stop-color="#FBC02D" offset="95.44%"/></radialGradient></defs><g fill="none" fill-rule="evenodd"><g transform="translate(3 4)"><mask id="ssvg-id-1st-place-medalc" fill="#fff"><use xlink:href="#ssvg-id-1st-place-medala"/></mask><path fill="url(#ssvg-id-1st-place-medalb)" fill-rule="nonzero" mask="url(#ssvg-id-1st-place-medalc)" d="M45.44 42.18h31.43l30-48.43H75.44z"/></g><g transform="translate(3 4)"><mask id="ssvg-id-1st-place-medale" fill="#fff"><use xlink:href="#ssvg-id-1st-place-medald"/></mask><g opacity=".2" mask="url(#ssvg-id-1st-place-medale)" fill="#424242" fill-rule="nonzero"><path d="M101.23-3L75.2 39H50.85L77.11-3h24.12zm5.64-3H75.44l-30 48h31.42l30.01-48z"/></g></g><g transform="translate(3 4)"><mask id="ssvg-id-1st-place-medalg" fill="#fff"><use xlink:href="#ssvg-id-1st-place-medalf"/></mask><path d="M79 30H43c-4.42 0-8 3.58-8 8v16.04c0 2.17 1.8 3.95 4.02 3.96h.01c2.23-.01 4.97-1.75 4.97-3.96V44c0-1.1.9-2 2-2h30c1.1 0 2 .9 2 2v9.93c0 1.98 2.35 3.68 4.22 4.04c.26.05.52.08.78.08c2.21 0 4-1.79 4-4V38c0-4.42-3.58-8-8-8z" fill="#FDD835" fill-rule="nonzero" mask="url(#ssvg-id-1st-place-medalg)"/></g><g transform="translate(3 4)"><mask id="ssvg-id-1st-place-medali" fill="#fff"><use xlink:href="#ssvg-id-1st-place-medalh"/></mask><g opacity=".2" mask="url(#ssvg-id-1st-place-medali)" fill="#424242" fill-rule="nonzero"><path d="M79 32c3.31 0 6 2.69 6 6v16.04A2.006 2.006 0 0 1 82.59 56c-1.18-.23-2.59-1.35-2.59-2.07V44c0-2.21-1.79-4-4-4H46c-2.21 0-4 1.79-4 4v10.04c0 .88-1.64 1.96-2.97 1.96c-1.12-.01-2.03-.89-2.03-1.96V38c0-3.31 2.69-6 6-6h36zm0-2H43c-4.42 0-8 3.58-8 8v16.04c0 2.17 1.8 3.95 4.02 3.96h.01c2.23-.01 4.97-1.75 4.97-3.96V44c0-1.1.9-2 2-2h30c1.1 0 2 .9 2 2v9.93c0 1.98 2.35 3.68 4.22 4.04c.26.05.52.08.78.08c2.21 0 4-1.79 4-4V38c0-4.42-3.58-8-8-8z"/></g></g><g transform="translate(3 4)"><mask id="ssvg-id-1st-place-medall" fill="#fff"><use xlink:href="#ssvg-id-1st-place-medalj"/></mask><path fill="url(#ssvg-id-1st-place-medalk)" fill-rule="nonzero" mask="url(#ssvg-id-1st-place-medall)" d="M76.87 42.18H45.44l-30-48.43h31.43z"/></g><g transform="translate(3 4)"><mask id="ssvg-id-1st-place-medaln" fill="#fff"><use xlink:href="#ssvg-id-1st-place-medalm"/></mask><g opacity=".2" mask="url(#ssvg-id-1st-place-medaln)" fill="#424242" fill-rule="nonzero"><path d="M45.1-3l26.35 42H47.1L20.86-3H45.1zm1.77-3H15.44l30 48h31.42L46.87-6z"/></g></g><circle fill="url(#ssvg-id-1st-place-medalo)" fill-rule="nonzero" cx="64" cy="86" r="38"/><path d="M64 51c19.3 0 35 15.7 35 35s-15.7 35-35 35s-35-15.7-35-35s15.7-35 35-35zm0-3c-20.99 0-38 17.01-38 38s17.01 38 38 38s38-17.01 38-38s-17.01-38-38-38z" opacity=".2" fill="#424242" fill-rule="nonzero"/><path d="M47.3 63.59h33.4v44.4H47.3z"/><use fill="#000" xlink:href="#ssvg-id-1st-place-medalp"/><use fill="#FFA000" xlink:href="#ssvg-id-1st-place-medalp"/></g>',
width: 128,
height: 128,
};
// Spacing for HTML matches
const spacing = (count) => {
return '\n' + ' '.repeat(count);
};
describe('Mounting component', () => {
test('with wrapper', () => {
const Wrapper = {
components: { IconifyIcon },
template: `<iconify-icon />`,
};
const wrapper = mount(Wrapper, {});
const item = wrapper.findComponent(IconifyIcon);
expect(item.exists()).toBe(true);
});
test('without wrapper', () => {
const wrapper = mount(IconifyIcon, {});
const item = wrapper.findComponent(IconifyIcon);
expect(item.exists()).toBe(true);
expect(item.html()).toStrictEqual(
'<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" aria-hidden="true" role="img" width="1em" height="1em" preserveAspectRatio="xMidYMid meet" viewBox="0 0 16 16"></svg>'
);
});
});
describe('Rendering icon', () => {
test('as object', () => {
const Wrapper = {
components: { IconifyIcon },
template: `<iconify-icon :icon='icon' />`,
data() {
return {
icon: iconData,
};
},
};
const wrapper = mount(Wrapper, {});
const item = wrapper.findComponent(IconifyIcon);
expect(item.exists()).toBe(true);
expect(item.html()).toStrictEqual(
'<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" aria-hidden="true" role="img" width="1em" height="1em" preserveAspectRatio="xMidYMid meet" viewBox="0 0 24 24">' +
spacing(1) +
'<path d="M4 19h16v2H4zm5-4h11v2H9zm-5-4h16v2H4zm0-8h16v2H4zm5 4h11v2H9z" fill="currentColor"></path>' +
spacing(0) +
'</svg>'
);
});
test('as string', () => {
const iconName = 'test-string';
IconifyIcon.addIcon(iconName, iconData);
const Wrapper = {
components: { IconifyIcon },
template: `<iconify-icon :icon='icon' />`,
data() {
return {
icon: iconName,
};
},
};
const wrapper = mount(Wrapper, {});
const item = wrapper.findComponent(IconifyIcon);
expect(item.exists()).toBe(true);
expect(item.html()).toStrictEqual(
'<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" aria-hidden="true" role="img" width="1em" height="1em" preserveAspectRatio="xMidYMid meet" viewBox="0 0 24 24">' +
spacing(1) +
'<path d="M4 19h16v2H4zm5-4h11v2H9zm-5-4h16v2H4zm0-8h16v2H4zm5 4h11v2H9z" fill="currentColor"></path>' +
spacing(0) +
'</svg>'
);
});
test('as string with icon set', () => {
const iconSet = {
prefix: 'mdi-light',
icons: {
account: {
body:
'<path d="M11.5 14c4.142 0 7.5 1.567 7.5 3.5V20H4v-2.5c0-1.933 3.358-3.5 7.5-3.5zm6.5 3.5c0-1.38-2.91-2.5-6.5-2.5S5 16.12 5 17.5V19h13v-1.5zM11.5 5a3.5 3.5 0 1 1 0 7a3.5 3.5 0 0 1 0-7zm0 1a2.5 2.5 0 1 0 0 5a2.5 2.5 0 0 0 0-5z" fill="currentColor"/>',
},
home: {
body:
'<path d="M16 8.414l-4.5-4.5L4.414 11H6v8h3v-6h5v6h3v-8h1.586L17 9.414V6h-1v2.414zM2 12l9.5-9.5L15 6V5h3v4l3 3h-3v7.998h-5v-6h-3v6H5V12H2z" fill="currentColor"/>',
},
},
width: 24,
height: 24,
};
IconifyIcon.addCollection(iconSet);
const Wrapper = {
components: { IconifyIcon },
template: `<iconify-icon icon='mdi-light:home' />`,
};
const wrapper = mount(Wrapper, {});
const item = wrapper.findComponent(IconifyIcon);
expect(item.exists()).toBe(true);
expect(item.html()).toStrictEqual(
'<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" aria-hidden="true" role="img" width="1em" height="1em" preserveAspectRatio="xMidYMid meet" viewBox="0 0 24 24">' +
spacing(1) +
'<path d="M16 8.414l-4.5-4.5L4.414 11H6v8h3v-6h5v6h3v-8h1.586L17 9.414V6h-1v2.414zM2 12l9.5-9.5L15 6V5h3v4l3 3h-3v7.998h-5v-6h-3v6H5V12H2z" fill="currentColor"></path>' +
spacing(0) +
'</svg>'
);
});
test('as string with icon set with custom prefix', () => {
const iconSet = {
prefix: 'mdi-light',
icons: {
'account-alert': {
body:
'<path d="M10.5 14c4.142 0 7.5 1.567 7.5 3.5V20H3v-2.5c0-1.933 3.358-3.5 7.5-3.5zm6.5 3.5c0-1.38-2.91-2.5-6.5-2.5S4 16.12 4 17.5V19h13v-1.5zM10.5 5a3.5 3.5 0 1 1 0 7a3.5 3.5 0 0 1 0-7zm0 1a2.5 2.5 0 1 0 0 5a2.5 2.5 0 0 0 0-5zM20 16v-1h1v1h-1zm0-3V7h1v6h-1z" fill="currentColor"/>',
},
'link': {
body:
'<path d="M8 13v-1h7v1H8zm7.5-6a5.5 5.5 0 1 1 0 11H13v-1h2.5a4.5 4.5 0 1 0 0-9H13V7h2.5zm-8 11a5.5 5.5 0 1 1 0-11H10v1H7.5a4.5 4.5 0 1 0 0 9H10v1H7.5z" fill="currentColor"/>',
},
},
width: 24,
height: 24,
};
IconifyIcon.addCollection(iconSet, 'custom-');
const Wrapper = {
components: { IconifyIcon },
template: `<iconify-icon icon='custom-link' />`,
};
const wrapper = mount(Wrapper, {});
const item = wrapper.findComponent(IconifyIcon);
expect(item.exists()).toBe(true);
expect(item.html()).toStrictEqual(
'<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" aria-hidden="true" role="img" width="1em" height="1em" preserveAspectRatio="xMidYMid meet" viewBox="0 0 24 24">' +
spacing(1) +
'<path d="M8 13v-1h7v1H8zm7.5-6a5.5 5.5 0 1 1 0 11H13v-1h2.5a4.5 4.5 0 1 0 0-9H13V7h2.5zm-8 11a5.5 5.5 0 1 1 0-11H10v1H7.5a4.5 4.5 0 1 0 0 9H10v1H7.5z" fill="currentColor"></path>' +
spacing(0) +
'</svg>'
);
});
test('replacing id', () => {
const Wrapper = {
components: { IconifyIcon },
template: `<iconify-icon :icon='icon' />`,
data() {
return {
icon: iconDataWithID,
};
},
};
const wrapper = mount(Wrapper, {});
const item = wrapper.findComponent(IconifyIcon);
expect(item.exists()).toBe(true);
expect(item.html()).not.toMatch('id="ssvg-id-1st-place-medala"');
});
});
describe('Passing attributes', () => {
test('title', () => {
const Wrapper = {
components: { IconifyIcon },
template: `<iconify-icon :icon='icon' title='Icon!' />`,
data() {
return {
icon: iconData,
};
},
};
const wrapper = mount(Wrapper, {});
const item = wrapper.findComponent(IconifyIcon);
expect(item.exists()).toBe(true);
expect(item.html()).toStrictEqual(
'<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" aria-hidden="true" role="img" title="Icon!" width="1em" height="1em" preserveAspectRatio="xMidYMid meet" viewBox="0 0 24 24">' +
spacing(1) +
'<path d="M4 19h16v2H4zm5-4h11v2H9zm-5-4h16v2H4zm0-8h16v2H4zm5 4h11v2H9z" fill="currentColor"></path>' +
spacing(0) +
'</svg>'
);
});
test('aria-hidden', () => {
const Wrapper = {
components: { IconifyIcon },
template: `<iconify-icon :icon='icon' :aria-hidden='false' />`,
data() {
return {
icon: iconData,
};
},
};
const wrapper = mount(Wrapper, {});
const item = wrapper.findComponent(IconifyIcon);
expect(item.exists()).toBe(true);
expect(item.html()).toStrictEqual(
'<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" role="img" width="1em" height="1em" preserveAspectRatio="xMidYMid meet" viewBox="0 0 24 24">' +
spacing(1) +
'<path d="M4 19h16v2H4zm5-4h11v2H9zm-5-4h16v2H4zm0-8h16v2H4zm5 4h11v2H9z" fill="currentColor"></path>' +
spacing(0) +
'</svg>'
);
});
test('ariaHidden', () => {
const Wrapper = {
components: { IconifyIcon },
template: `<iconify-icon :icon='icon' :ariaHidden='false' />`,
data() {
return {
icon: iconData,
};
},
};
const wrapper = mount(Wrapper, {});
const item = wrapper.findComponent(IconifyIcon);
expect(item.exists()).toBe(true);
expect(item.html()).toStrictEqual(
'<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" role="img" width="1em" height="1em" preserveAspectRatio="xMidYMid meet" viewBox="0 0 24 24">' +
spacing(1) +
'<path d="M4 19h16v2H4zm5-4h11v2H9zm-5-4h16v2H4zm0-8h16v2H4zm5 4h11v2H9z" fill="currentColor"></path>' +
spacing(0) +
'</svg>'
);
});
test('attributes that cannot change', () => {
const Wrapper = {
components: { IconifyIcon },
template: `<iconify-icon :icon='icon' viewBox='0 0 0 0' preserveAspectRatio='none' />`,
data() {
return {
icon: iconData,
};
},
};
const wrapper = mount(Wrapper, {});
const item = wrapper.findComponent(IconifyIcon);
expect(item.exists()).toBe(true);
expect(item.html()).toStrictEqual(
// same values, but different order
'<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" aria-hidden="true" role="img" viewBox="0 0 24 24" preserveAspectRatio="xMidYMid meet" width="1em" height="1em">' +
spacing(1) +
'<path d="M4 19h16v2H4zm5-4h11v2H9zm-5-4h16v2H4zm0-8h16v2H4zm5 4h11v2H9z" fill="currentColor"></path>' +
spacing(0) +
'</svg>'
);
});
});
describe('Dimensions', () => {
test('height', () => {
const Wrapper = {
components: { IconifyIcon },
template: `<iconify-icon :icon='icon' :height='height' />`,
data() {
return {
icon: iconData,
height: 24,
};
},
};
const wrapper = mount(Wrapper, {});
const item = wrapper.findComponent(IconifyIcon);
expect(item.exists()).toBe(true);
expect(item.html()).toStrictEqual(
'<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" aria-hidden="true" role="img" width="24" height="24" preserveAspectRatio="xMidYMid meet" viewBox="0 0 24 24">' +
spacing(1) +
'<path d="M4 19h16v2H4zm5-4h11v2H9zm-5-4h16v2H4zm0-8h16v2H4zm5 4h11v2H9z" fill="currentColor"></path>' +
spacing(0) +
'</svg>'
);
});
test('width and height', () => {
const Wrapper = {
components: { IconifyIcon },
template: `<iconify-icon :icon='icon' :width='width' :height='height' />`,
data() {
return {
icon: iconData,
width: 32, // as number
height: '48', // as string
};
},
};
const wrapper = mount(Wrapper, {});
const item = wrapper.findComponent(IconifyIcon);
expect(item.exists()).toBe(true);
expect(item.html()).toStrictEqual(
'<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" aria-hidden="true" role="img" width="32" height="48" preserveAspectRatio="xMidYMid meet" viewBox="0 0 24 24">' +
spacing(1) +
'<path d="M4 19h16v2H4zm5-4h11v2H9zm-5-4h16v2H4zm0-8h16v2H4zm5 4h11v2H9z" fill="currentColor"></path>' +
spacing(0) +
'</svg>'
);
});
test('auto', () => {
const Wrapper = {
components: { IconifyIcon },
template: `<iconify-icon :icon='icon' :height='height' />`,
data() {
return {
icon: iconData,
height: 'auto',
};
},
};
const wrapper = mount(Wrapper, {});
const item = wrapper.findComponent(IconifyIcon);
expect(item.exists()).toBe(true);
expect(item.html()).toStrictEqual(
'<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" aria-hidden="true" role="img" width="24" height="24" preserveAspectRatio="xMidYMid meet" viewBox="0 0 24 24">' +
spacing(1) +
'<path d="M4 19h16v2H4zm5-4h11v2H9zm-5-4h16v2H4zm0-8h16v2H4zm5 4h11v2H9z" fill="currentColor"></path>' +
spacing(0) +
'</svg>'
);
});
});
describe('Rotation', () => {
test('number', () => {
const Wrapper = {
components: { IconifyIcon },
template: `<iconify-icon :icon='icon' :rotate='rotate' />`,
data() {
return {
icon: iconData,
rotate: 1,
};
},
};
const wrapper = mount(Wrapper, {});
const item = wrapper.findComponent(IconifyIcon);
expect(item.exists()).toBe(true);
expect(item.html()).toMatch('rotate(90 ');
});
test('string', () => {
const Wrapper = {
components: { IconifyIcon },
template: `<iconify-icon :icon='icon' rotate='270deg' />`,
data() {
return {
icon: iconData,
};
},
};
const wrapper = mount(Wrapper, {});
const item = wrapper.findComponent(IconifyIcon);
expect(item.exists()).toBe(true);
expect(item.html()).toMatch('rotate(-90 ');
});
});
describe('Flip', () => {
test('boolean', () => {
const Wrapper = {
components: { IconifyIcon },
template: `<iconify-icon :icon='icon' :horizontalFlip='horizontalFlip' />`,
data() {
return {
icon: iconData,
horizontalFlip: true,
};
},
};
const wrapper = mount(Wrapper, {});
const item = wrapper.findComponent(IconifyIcon);
expect(item.exists()).toBe(true);
expect(item.html()).toMatch('scale(-1 1)');
});
test('string', () => {
const Wrapper = {
components: { IconifyIcon },
template: `<iconify-icon :icon='icon' flip='vertical' />`,
data() {
return {
icon: iconData,
};
},
};
const wrapper = mount(Wrapper, {});
const item = wrapper.findComponent(IconifyIcon);
expect(item.exists()).toBe(true);
expect(item.html()).toMatch('scale(1 -1)');
});
test('string and boolean', () => {
const Wrapper = {
components: { IconifyIcon },
template: `<iconify-icon :icon='icon' flip='horizontal' :verticalFlip='true' />`,
data() {
return {
icon: iconData,
};
},
};
const wrapper = mount(Wrapper, {});
const item = wrapper.findComponent(IconifyIcon);
expect(item.exists()).toBe(true);
// horizontal + vertical = 180deg rotation
expect(item.html()).toMatch('rotate(180 ');
});
test('string for boolean attribute', () => {
const Wrapper = {
components: { IconifyIcon },
template: `<iconify-icon :icon='icon' horizontalFlip='true' />`,
data() {
return {
icon: iconData,
};
},
};
const wrapper = mount(Wrapper, {});
const item = wrapper.findComponent(IconifyIcon);
expect(item.exists()).toBe(true);
expect(item.html()).toMatch('scale(-1 1)');
});
test('shorthand and boolean', () => {
// 'flip' is processed after 'hFlip', overwriting value
const Wrapper = {
components: { IconifyIcon },
template: `<iconify-icon :icon='icon' flip='horizontal' :hFlip='false' />`,
data() {
return {
icon: iconData,
};
},
};
const wrapper = mount(Wrapper, {});
const item = wrapper.findComponent(IconifyIcon);
expect(item.exists()).toBe(true);
expect(item.html()).toMatch('scale(-1 1)');
});
test('shorthand and boolean as string', () => {
const Wrapper = {
components: { IconifyIcon },
template: `<iconify-icon :icon='icon' flip='vertical' horizontalFlip='true' />`,
data() {
return {
icon: iconData,
};
},
};
const wrapper = mount(Wrapper, {});
const item = wrapper.findComponent(IconifyIcon);
expect(item.exists()).toBe(true);
// horizontal + vertical = 180deg rotation
expect(item.html()).toMatch('rotate(180 ');
});
test('wrong case', () => {
const Wrapper = {
components: { IconifyIcon },
template: `<iconify-icon :icon='icon' :verticalflip='verticalflip' :horizontalflip='true' />`,
data() {
return {
icon: iconData,
verticalflip: true,
};
},
};
const wrapper = mount(Wrapper, {});
const item = wrapper.findComponent(IconifyIcon);
expect(item.exists()).toBe(true);
expect(item.html()).not.toMatch('scale(');
expect(item.html()).not.toMatch('rotate(');
});
});
describe('Alignment and slice', () => {
test('vAlign and slice', () => {
const Wrapper = {
components: { IconifyIcon },
template: `<iconify-icon :icon='icon' vAlign='top' :slice='true' />`,
data() {
return {
icon: iconData,
};
},
};
const wrapper = mount(Wrapper, {});
const item = wrapper.findComponent(IconifyIcon);
expect(item.exists()).toBe(true);
expect(item.html()).toMatch('preserveAspectRatio="xMidYMin slice"');
});
test('string', () => {
const Wrapper = {
components: { IconifyIcon },
template: `<iconify-icon :icon='icon' align='left bottom' />`,
data() {
return {
icon: iconData,
};
},
};
const wrapper = mount(Wrapper, {});
const item = wrapper.findComponent(IconifyIcon);
expect(item.exists()).toBe(true);
expect(item.html()).toMatch('preserveAspectRatio="xMinYMax meet"');
});
test('Alignment aliases', () => {
const Wrapper = {
components: { IconifyIcon },
template: `<iconify-icon :icon='icon' verticalAlign='top' horizontalAlign='right' />`,
data() {
return {
icon: iconData,
};
},
};
const wrapper = mount(Wrapper, {});
const item = wrapper.findComponent(IconifyIcon);
expect(item.exists()).toBe(true);
expect(item.html()).toMatch('preserveAspectRatio="xMaxYMin meet"');
});
});

View File

@ -0,0 +1,40 @@
import { mount } from '@vue/test-utils';
import { Icon } from '../../dist/iconify';
const iconData = {
body:
'<path d="M4 19h16v2H4zm5-4h11v2H9zm-5-4h16v2H4zm0-8h16v2H4zm5 4h11v2H9z" fill="currentColor"/>',
width: 24,
height: 24,
};
describe('Creating component', () => {
test('with wrapper', () => {
const Wrapper = {
components: { Icon },
template: `<Icon :icon='icon' />`,
data() {
return {
icon: iconData,
};
},
};
const wrapper = mount(Wrapper, {});
expect(wrapper.html().replace(/\s*\n\s*/g, '')).toEqual(
'<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" aria-hidden="true" role="img" width="1em" height="1em" preserveAspectRatio="xMidYMid meet" viewBox="0 0 24 24"><path d="M4 19h16v2H4zm5-4h11v2H9zm-5-4h16v2H4zm0-8h16v2H4zm5 4h11v2H9z" fill="currentColor"></path></svg>'
);
});
test('without wrapper', () => {
const wrapper = mount(Icon, {
propsData: {
icon: iconData,
},
});
expect(wrapper.html().replace(/\s*\n\s*/g, '')).toEqual(
'<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" aria-hidden="true" role="img" width="1em" height="1em" preserveAspectRatio="xMidYMid meet" viewBox="0 0 24 24"><path d="M4 19h16v2H4zm5-4h11v2H9zm-5-4h16v2H4zm0-8h16v2H4zm5 4h11v2H9z" fill="currentColor"></path></svg>'
);
});
});

View File

@ -0,0 +1,46 @@
import { mount } from '@vue/test-utils';
import { Icon } from '../../dist/iconify';
describe('Empty icon', () => {
test('basic test', () => {
const wrapper = mount(Icon, {
propsData: {},
});
expect(wrapper.html().replace(/\s*\n\s*/g, '')).toEqual('');
});
test('with child node', () => {
const Wrapper = {
components: { Icon },
template: `<Icon><i class="fa fa-home" /></Icon>`,
};
const wrapper = mount(Wrapper, {});
expect(wrapper.html().replace(/\s*\n\s*/g, '')).toEqual(
'<i class="fa fa-home"></i>'
);
});
test('with text child node', () => {
const Wrapper = {
components: { Icon },
template: `<Icon>icon</Icon>`,
};
const wrapper = mount(Wrapper, {});
expect(wrapper.text()).toEqual('icon');
});
test('with multiple childen', () => {
const Wrapper = {
components: { Icon },
template: `<Icon><i class="fa fa-home" /> Home icon</Icon>`,
};
const wrapper = mount(Wrapper, {});
expect(wrapper.html().replace(/\s*\n\s*/g, '')).toEqual(
'<span><i class="fa fa-home"></i> Home icon</span>'
);
});
});

View File

@ -0,0 +1,144 @@
import { mount } from '@vue/test-utils';
import { Icon } from '../../dist/iconify';
const iconData = {
body:
'<path d="M4 19h16v2H4zm5-4h11v2H9zm-5-4h16v2H4zm0-8h16v2H4zm5 4h11v2H9z" fill="currentColor"/>',
width: 24,
height: 24,
};
describe('Passing attributes', () => {
test('title', () => {
const Wrapper = {
components: { Icon },
template: `<Icon :icon="icon" title="Icon!" />`,
data() {
return {
icon: iconData,
};
},
};
const wrapper = mount(Wrapper, {});
const html = wrapper.html();
expect(html).toContain('role="img" title="Icon!"');
// Make sure aria-hidden exists (for tests below)
expect(html).toContain('aria-hidden="true"');
});
test('aria-hidden', () => {
// dashes, string value
const Wrapper = {
components: { Icon },
template: `<Icon :icon="icon" aria-hidden="false" />`,
data() {
return {
icon: iconData,
};
},
};
const wrapper = mount(Wrapper, {});
expect(wrapper.html()).not.toContain('aria-hidden="true"');
});
test('ariaHidden', () => {
// camelCase, boolean value
const Wrapper = {
components: { Icon },
template: `<Icon :icon="icon" :ariaHidden="false" />`,
data() {
return {
icon: iconData,
};
},
};
const wrapper = mount(Wrapper, {});
expect(wrapper.html()).not.toContain('aria-hidden="true"');
});
test('style as string', () => {
const Wrapper = {
components: { Icon },
template: `<Icon :icon="icon" style="color: red;" />`,
data() {
return {
icon: iconData,
};
},
};
const wrapper = mount(Wrapper, {});
expect(wrapper.html()).toContain('style="color: red;"');
});
test('style as object', () => {
const Wrapper = {
components: { Icon },
template: `<Icon :icon="icon" :style="style" />`,
data() {
return {
icon: iconData,
style: {
color: 'red',
},
};
},
};
const wrapper = mount(Wrapper, {});
expect(wrapper.html()).toContain('style="color: red;"');
});
test('color', () => {
const Wrapper = {
components: { Icon },
template: `<Icon :icon="icon" color="red" />`,
data() {
return {
icon: iconData,
};
},
};
const wrapper = mount(Wrapper, {});
expect(wrapper.html()).toContain('style="color: red;"');
});
test('color with style', () => {
// In Vue 2 style overrides color in class components
// Can be fixed by using functional component, but functional component is not possible with API
const Wrapper = {
components: { Icon },
template: `<Icon :icon="icon" color="purple" style="color: green;" />`,
data() {
return {
icon: iconData,
};
},
};
const wrapper = mount(Wrapper, {});
expect(wrapper.html()).toContain('style="color: green;"');
});
test('attributes that cannot change', () => {
const Wrapper = {
components: { Icon },
template: `<Icon :icon="icon" viewBox="0 0 0 0" preserveAspectRatio="none" />`,
data() {
return {
icon: iconData,
};
},
};
const wrapper = mount(Wrapper, {});
const html = wrapper.html();
expect(html).not.toContain('viewBox="0 0 0 0"');
expect(html).not.toContain('preserveAspectRatio="none"');
});
});

View File

@ -0,0 +1,68 @@
import { mount } from '@vue/test-utils';
import { Icon } from '../../dist/iconify';
const iconData = {
body:
'<path d="M4 19h16v2H4zm5-4h11v2H9zm-5-4h16v2H4zm0-8h16v2H4zm5 4h11v2H9z" fill="currentColor"/>',
width: 24,
height: 24,
};
describe('Dimensions', () => {
test('height', () => {
const Wrapper = {
components: { Icon },
template: `<Icon :icon="icon" height="48" />`,
data() {
return {
icon: iconData,
};
},
};
const wrapper = mount(Wrapper, {});
const html = wrapper.html();
expect(html).toContain('height="48"');
expect(html).toContain('width="48"');
expect(html).not.toContain('height="1em"');
expect(html).not.toContain('width="1em"');
});
test('width and height', () => {
const Wrapper = {
components: { Icon },
template: `<Icon :icon="icon" :width="32" height="48" />`,
data() {
return {
icon: iconData,
};
},
};
const wrapper = mount(Wrapper, {});
const html = wrapper.html();
expect(html).toContain('height="48"');
expect(html).toContain('width="32"');
expect(html).not.toContain('height="1em"');
expect(html).not.toContain('width="1em"');
});
test('auto', () => {
const Wrapper = {
components: { Icon },
template: `<Icon :icon="icon" height="auto" />`,
data() {
return {
icon: iconData,
};
},
};
const wrapper = mount(Wrapper, {});
const html = wrapper.html();
expect(html).toContain('height="24"');
expect(html).toContain('width="24"');
expect(html).not.toContain('height="1em"');
expect(html).not.toContain('width="1em"');
});
});

View File

@ -0,0 +1,41 @@
import { mount } from '@vue/test-utils';
import { Icon } from '../../dist/iconify';
const iconDataWithID = {
body:
'<defs><path id="ssvg-id-1st-place-medala" d="M.93.01h120.55v58.36H.93z"/><path id="ssvg-id-1st-place-medald" d="M.93.01h120.55v58.36H.93z"/><path id="ssvg-id-1st-place-medalf" d="M.93.01h120.55v58.36H.93z"/><path id="ssvg-id-1st-place-medalh" d="M.93.01h120.55v58.36H.93z"/><path id="ssvg-id-1st-place-medalj" d="M.93.01h120.55v58.36H.93z"/><path id="ssvg-id-1st-place-medalm" d="M.93.01h120.55v58.36H.93z"/><path d="M52.849 78.373v-3.908c3.681-.359 6.25-.958 7.703-1.798c1.454-.84 2.54-2.828 3.257-5.962h4.021v40.385h-5.437V78.373h-9.544z" id="ssvg-id-1st-place-medalp"/><linearGradient x1="49.998%" y1="-13.249%" x2="49.998%" y2="90.002%" id="ssvg-id-1st-place-medalb"><stop stop-color="#1E88E5" offset="13.55%"/><stop stop-color="#1565C0" offset="93.8%"/></linearGradient><linearGradient x1="26.648%" y1="2.735%" x2="77.654%" y2="105.978%" id="ssvg-id-1st-place-medalk"><stop stop-color="#64B5F6" offset="13.55%"/><stop stop-color="#2196F3" offset="94.62%"/></linearGradient><radialGradient cx="22.368%" cy="12.5%" fx="22.368%" fy="12.5%" r="95.496%" id="ssvg-id-1st-place-medalo"><stop stop-color="#FFEB3B" offset="29.72%"/><stop stop-color="#FBC02D" offset="95.44%"/></radialGradient></defs><g fill="none" fill-rule="evenodd"><g transform="translate(3 4)"><mask id="ssvg-id-1st-place-medalc" fill="#fff"><use xlink:href="#ssvg-id-1st-place-medala"/></mask><path fill="url(#ssvg-id-1st-place-medalb)" fill-rule="nonzero" mask="url(#ssvg-id-1st-place-medalc)" d="M45.44 42.18h31.43l30-48.43H75.44z"/></g><g transform="translate(3 4)"><mask id="ssvg-id-1st-place-medale" fill="#fff"><use xlink:href="#ssvg-id-1st-place-medald"/></mask><g opacity=".2" mask="url(#ssvg-id-1st-place-medale)" fill="#424242" fill-rule="nonzero"><path d="M101.23-3L75.2 39H50.85L77.11-3h24.12zm5.64-3H75.44l-30 48h31.42l30.01-48z"/></g></g><g transform="translate(3 4)"><mask id="ssvg-id-1st-place-medalg" fill="#fff"><use xlink:href="#ssvg-id-1st-place-medalf"/></mask><path d="M79 30H43c-4.42 0-8 3.58-8 8v16.04c0 2.17 1.8 3.95 4.02 3.96h.01c2.23-.01 4.97-1.75 4.97-3.96V44c0-1.1.9-2 2-2h30c1.1 0 2 .9 2 2v9.93c0 1.98 2.35 3.68 4.22 4.04c.26.05.52.08.78.08c2.21 0 4-1.79 4-4V38c0-4.42-3.58-8-8-8z" fill="#FDD835" fill-rule="nonzero" mask="url(#ssvg-id-1st-place-medalg)"/></g><g transform="translate(3 4)"><mask id="ssvg-id-1st-place-medali" fill="#fff"><use xlink:href="#ssvg-id-1st-place-medalh"/></mask><g opacity=".2" mask="url(#ssvg-id-1st-place-medali)" fill="#424242" fill-rule="nonzero"><path d="M79 32c3.31 0 6 2.69 6 6v16.04A2.006 2.006 0 0 1 82.59 56c-1.18-.23-2.59-1.35-2.59-2.07V44c0-2.21-1.79-4-4-4H46c-2.21 0-4 1.79-4 4v10.04c0 .88-1.64 1.96-2.97 1.96c-1.12-.01-2.03-.89-2.03-1.96V38c0-3.31 2.69-6 6-6h36zm0-2H43c-4.42 0-8 3.58-8 8v16.04c0 2.17 1.8 3.95 4.02 3.96h.01c2.23-.01 4.97-1.75 4.97-3.96V44c0-1.1.9-2 2-2h30c1.1 0 2 .9 2 2v9.93c0 1.98 2.35 3.68 4.22 4.04c.26.05.52.08.78.08c2.21 0 4-1.79 4-4V38c0-4.42-3.58-8-8-8z"/></g></g><g transform="translate(3 4)"><mask id="ssvg-id-1st-place-medall" fill="#fff"><use xlink:href="#ssvg-id-1st-place-medalj"/></mask><path fill="url(#ssvg-id-1st-place-medalk)" fill-rule="nonzero" mask="url(#ssvg-id-1st-place-medall)" d="M76.87 42.18H45.44l-30-48.43h31.43z"/></g><g transform="translate(3 4)"><mask id="ssvg-id-1st-place-medaln" fill="#fff"><use xlink:href="#ssvg-id-1st-place-medalm"/></mask><g opacity=".2" mask="url(#ssvg-id-1st-place-medaln)" fill="#424242" fill-rule="nonzero"><path d="M45.1-3l26.35 42H47.1L20.86-3H45.1zm1.77-3H15.44l30 48h31.42L46.87-6z"/></g></g><circle fill="url(#ssvg-id-1st-place-medalo)" fill-rule="nonzero" cx="64" cy="86" r="38"/><path d="M64 51c19.3 0 35 15.7 35 35s-15.7 35-35 35s-35-15.7-35-35s15.7-35 35-35zm0-3c-20.99 0-38 17.01-38 38s17.01 38 38 38s38-17.01 38-38s-17.01-38-38-38z" opacity=".2" fill="#424242" fill-rule="nonzero"/><path d="M47.3 63.59h33.4v44.4H47.3z"/><use fill="#000" xlink:href="#ssvg-id-1st-place-medalp"/><use fill="#FFA000" xlink:href="#ssvg-id-1st-place-medalp"/></g>',
width: 128,
height: 128,
};
describe('Replacing IDs', () => {
test('default behavior', () => {
const Wrapper = {
components: { Icon },
template: `<Icon :icon="icon" />`,
data() {
return {
icon: iconDataWithID,
};
},
};
const wrapper = mount(Wrapper, {});
expect(wrapper.html()).not.toContain('ssvg-id-1st-place-medala');
});
test('custom generator', () => {
const Wrapper = {
components: { Icon },
template: `<Icon :icon="icon" id="test" />`,
data() {
return {
icon: iconDataWithID,
};
},
};
const wrapper = mount(Wrapper, {});
expect(wrapper.html()).toContain('id="test-0"');
});
});

View File

@ -0,0 +1,135 @@
import { mount } from '@vue/test-utils';
import { Icon } from '../../dist/iconify';
const iconData = {
body:
'<path d="M4 19h16v2H4zm5-4h11v2H9zm-5-4h16v2H4zm0-8h16v2H4zm5 4h11v2H9z" fill="currentColor"/>',
width: 24,
height: 24,
};
describe('Inline attribute', () => {
test('string', () => {
const Wrapper = {
components: { Icon },
template: `<Icon :icon="icon" inline="true" />`,
data() {
return {
icon: iconData,
};
},
};
const wrapper = mount(Wrapper, {});
expect(wrapper.html()).toContain('style="vertical-align: -0.125em;"');
});
test('false string', () => {
// "false" = true
const Wrapper = {
components: { Icon },
template: `<Icon :icon="icon" inline="false" />`,
data() {
return {
icon: iconData,
};
},
};
const wrapper = mount(Wrapper, {});
expect(wrapper.html()).toContain('style="vertical-align: -0.125em;"');
});
test('true', () => {
const Wrapper = {
components: { Icon },
template: `<Icon :icon="icon" :inline="true" />`,
data() {
return {
icon: iconData,
};
},
};
const wrapper = mount(Wrapper, {});
expect(wrapper.html()).toContain('style="vertical-align: -0.125em;"');
});
test('false', () => {
const Wrapper = {
components: { Icon },
template: `<Icon :icon="icon" :inline="false" />`,
data() {
return {
icon: iconData,
};
},
};
const wrapper = mount(Wrapper, {});
expect(wrapper.html()).not.toContain(
'vertical-align: -0.125em; color: red;'
);
});
test('inline and style string', () => {
// Style goes after vertical-align
const Wrapper = {
components: { Icon },
template: `<Icon :icon="icon" :inline="true" style="color: red;" />`,
data() {
return {
icon: iconData,
};
},
};
const wrapper = mount(Wrapper, {});
expect(wrapper.html()).toContain(
'vertical-align: -0.125em; color: red;'
);
});
test('inline and style object', () => {
// Style goes after vertical-align
const Wrapper = {
components: { Icon },
template: `<Icon :icon="icon" :inline="true" :style="style" />`,
data() {
return {
icon: iconData,
style: {
color: 'red',
},
};
},
};
const wrapper = mount(Wrapper, {});
expect(wrapper.html()).toContain(
'vertical-align: -0.125em; color: red;'
);
});
test('inline and style overriding it', () => {
// Style goes after vertical-align
const Wrapper = {
components: { Icon },
template: `<Icon :icon="icon" :inline="true" :style="style" />`,
data() {
return {
icon: iconData,
style: {
verticalAlign: 0,
},
};
},
};
const wrapper = mount(Wrapper, {});
// For some reason style is duplicated
expect(wrapper.html()).toContain(
'style="vertical-align: 0; vertical-align: 0;"'
);
});
});

View File

@ -0,0 +1,208 @@
import { mount } from '@vue/test-utils';
import { Icon } from '../../dist/iconify';
const iconData = {
body:
'<path d="M4 19h16v2H4zm5-4h11v2H9zm-5-4h16v2H4zm0-8h16v2H4zm5 4h11v2H9z" fill="currentColor"/>',
width: 24,
height: 24,
};
describe('Rotation', () => {
test('number', () => {
const Wrapper = {
components: { Icon },
template: `<Icon :icon="icon" :rotate="1" />`,
data() {
return {
icon: iconData,
};
},
};
const wrapper = mount(Wrapper, {});
expect(wrapper.html().replace(/\s*\n\s*/g, '')).toEqual(
'<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" aria-hidden="true" role="img" width="1em" height="1em" preserveAspectRatio="xMidYMid meet" viewBox="0 0 24 24"><g transform="rotate(90 12 12)"><path d="M4 19h16v2H4zm5-4h11v2H9zm-5-4h16v2H4zm0-8h16v2H4zm5 4h11v2H9z" fill="currentColor"></path></g></svg>'
);
});
test('string', () => {
const Wrapper = {
components: { Icon },
template: `<Icon :icon="icon" rotate="180deg" />`,
data() {
return {
icon: iconData,
};
},
};
const wrapper = mount(Wrapper, {});
expect(wrapper.html()).toContain('<g transform="rotate(180 12 12)">');
});
});
describe('Flip', () => {
test('boolean', () => {
const Wrapper = {
components: { Icon },
template: `<Icon :icon="icon" :hFlip="true" />`,
data() {
return {
icon: iconData,
};
},
};
const wrapper = mount(Wrapper, {});
expect(wrapper.html()).toContain(
'<g transform="translate(24 0) scale(-1 1)">'
);
});
test('string', () => {
const Wrapper = {
components: { Icon },
template: `<Icon :icon="icon" flip="vertical" />`,
data() {
return {
icon: iconData,
};
},
};
const wrapper = mount(Wrapper, {});
expect(wrapper.html()).toContain(
'<g transform="translate(0 24) scale(1 -1)">'
);
});
test('string and boolean', () => {
const Wrapper = {
components: { Icon },
template: `<Icon :icon="icon" flip="horizontal" :vFlip="true" />`,
data() {
return {
icon: iconData,
};
},
};
const wrapper = mount(Wrapper, {});
expect(wrapper.html()).toContain('<g transform="rotate(180 12 12)">');
});
test('string for boolean attribute', () => {
const Wrapper = {
components: { Icon },
template: `<Icon :icon="icon" horizontal-flip="true" />`,
data() {
return {
icon: iconData,
};
},
};
const wrapper = mount(Wrapper, {});
expect(wrapper.html()).toContain(
'<g transform="translate(24 0) scale(-1 1)">'
);
});
test('shorthand and boolean', () => {
// 'flip' is processed after 'hFlip', overwriting value
const Wrapper = {
components: { Icon },
template: `<Icon :icon="icon" flip="horizontal" :hFlip="false" />`,
data() {
return {
icon: iconData,
};
},
};
const wrapper = mount(Wrapper, {});
expect(wrapper.html()).toContain(
'<g transform="translate(24 0) scale(-1 1)">'
);
});
test('shorthand and boolean as string', () => {
const Wrapper = {
components: { Icon },
template: `<Icon :icon="icon" flip="vertical" :horizontal-flip="true" />`,
data() {
return {
icon: iconData,
};
},
};
const wrapper = mount(Wrapper, {});
expect(wrapper.html()).toContain('<g transform="rotate(180 12 12)">');
});
test('wrong case', () => {
const Wrapper = {
components: { Icon },
template: `<Icon :icon="icon" :vflip="true" />`,
data() {
return {
icon: iconData,
};
},
};
const wrapper = mount(Wrapper, {});
expect(wrapper.html()).not.toContain('<g transform="');
});
});
describe('Alignment and slice', () => {
test('vAlign and slice', () => {
const Wrapper = {
components: { Icon },
template: `<Icon :icon="icon" vAlign="top" :slice="true" />`,
data() {
return {
icon: iconData,
};
},
};
const wrapper = mount(Wrapper, {});
expect(wrapper.html()).toContain(
'preserveAspectRatio="xMidYMin slice"'
);
});
test('string', () => {
const Wrapper = {
components: { Icon },
template: `<Icon :icon="icon" align="left bottom" />`,
data() {
return {
icon: iconData,
};
},
};
const wrapper = mount(Wrapper, {});
expect(wrapper.html()).toContain('preserveAspectRatio="xMinYMax meet"');
});
test('aliases', () => {
const Wrapper = {
components: { Icon },
template: `<Icon :icon="icon" h-align="left" vertical-align="bottom" />`,
data() {
return {
icon: iconData,
};
},
};
const wrapper = mount(Wrapper, {});
expect(wrapper.html()).toContain('preserveAspectRatio="xMinYMax meet"');
});
});

View File

@ -0,0 +1,40 @@
import { mount } from '@vue/test-utils';
import { Icon } from '../../dist/offline';
const iconData = {
body:
'<path d="M4 19h16v2H4zm5-4h11v2H9zm-5-4h16v2H4zm0-8h16v2H4zm5 4h11v2H9z" fill="currentColor"/>',
width: 24,
height: 24,
};
describe('Creating component', () => {
test('with wrapper', () => {
const Wrapper = {
components: { Icon },
template: `<Icon :icon='icon' />`,
data() {
return {
icon: iconData,
};
},
};
const wrapper = mount(Wrapper, {});
expect(wrapper.html().replace(/\s*\n\s*/g, '')).toEqual(
'<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" aria-hidden="true" role="img" width="1em" height="1em" preserveAspectRatio="xMidYMid meet" viewBox="0 0 24 24"><path d="M4 19h16v2H4zm5-4h11v2H9zm-5-4h16v2H4zm0-8h16v2H4zm5 4h11v2H9z" fill="currentColor"></path></svg>'
);
});
test('without wrapper', () => {
const wrapper = mount(Icon, {
propsData: {
icon: iconData,
},
});
expect(wrapper.html().replace(/\s*\n\s*/g, '')).toEqual(
'<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" aria-hidden="true" role="img" width="1em" height="1em" preserveAspectRatio="xMidYMid meet" viewBox="0 0 24 24"><path d="M4 19h16v2H4zm5-4h11v2H9zm-5-4h16v2H4zm0-8h16v2H4zm5 4h11v2H9z" fill="currentColor"></path></svg>'
);
});
});

View File

@ -0,0 +1,46 @@
import { mount } from '@vue/test-utils';
import { Icon } from '../../dist/offline';
describe('Empty icon', () => {
test('basic test', () => {
const wrapper = mount(Icon, {
propsData: {},
});
expect(wrapper.html().replace(/\s*\n\s*/g, '')).toEqual('');
});
test('with child node', () => {
const Wrapper = {
components: { Icon },
template: `<Icon><i class="fa fa-home" /></Icon>`,
};
const wrapper = mount(Wrapper, {});
expect(wrapper.html().replace(/\s*\n\s*/g, '')).toEqual(
'<i class="fa fa-home"></i>'
);
});
test('with text child node', () => {
const Wrapper = {
components: { Icon },
template: `<Icon>icon</Icon>`,
};
const wrapper = mount(Wrapper, {});
expect(wrapper.text()).toEqual('icon');
});
test('with multiple childen', () => {
const Wrapper = {
components: { Icon },
template: `<Icon><i class="fa fa-home" /> Home icon</Icon>`,
};
const wrapper = mount(Wrapper, {});
expect(wrapper.html().replace(/\s*\n\s*/g, '')).toEqual(
'<span><i class="fa fa-home"></i> Home icon</span>'
);
});
});

View File

@ -0,0 +1,144 @@
import { mount } from '@vue/test-utils';
import { Icon } from '../../dist/offline';
const iconData = {
body:
'<path d="M4 19h16v2H4zm5-4h11v2H9zm-5-4h16v2H4zm0-8h16v2H4zm5 4h11v2H9z" fill="currentColor"/>',
width: 24,
height: 24,
};
describe('Passing attributes', () => {
test('title', () => {
const Wrapper = {
components: { Icon },
template: `<Icon :icon="icon" title="Icon!" />`,
data() {
return {
icon: iconData,
};
},
};
const wrapper = mount(Wrapper, {});
const html = wrapper.html();
expect(html).toContain('role="img" title="Icon!"');
// Make sure aria-hidden exists (for tests below)
expect(html).toContain('aria-hidden="true"');
});
test('aria-hidden', () => {
// dashes, string value
const Wrapper = {
components: { Icon },
template: `<Icon :icon="icon" aria-hidden="false" />`,
data() {
return {
icon: iconData,
};
},
};
const wrapper = mount(Wrapper, {});
expect(wrapper.html()).not.toContain('aria-hidden="true"');
});
test('ariaHidden', () => {
// camelCase, boolean value
const Wrapper = {
components: { Icon },
template: `<Icon :icon="icon" :ariaHidden="false" />`,
data() {
return {
icon: iconData,
};
},
};
const wrapper = mount(Wrapper, {});
expect(wrapper.html()).not.toContain('aria-hidden="true"');
});
test('style as string', () => {
const Wrapper = {
components: { Icon },
template: `<Icon :icon="icon" style="color: red;" />`,
data() {
return {
icon: iconData,
};
},
};
const wrapper = mount(Wrapper, {});
expect(wrapper.html()).toContain('style="color: red;"');
});
test('style as object', () => {
const Wrapper = {
components: { Icon },
template: `<Icon :icon="icon" :style="style" />`,
data() {
return {
icon: iconData,
style: {
color: 'red',
},
};
},
};
const wrapper = mount(Wrapper, {});
expect(wrapper.html()).toContain('style="color: red;"');
});
test('color', () => {
const Wrapper = {
components: { Icon },
template: `<Icon :icon="icon" color="red" />`,
data() {
return {
icon: iconData,
};
},
};
const wrapper = mount(Wrapper, {});
expect(wrapper.html()).toContain('style="color: red;"');
});
test('color with style', () => {
// In Vue 2 style overrides color in class components
// Can be fixed by using functional component, but functional component is not possible with API
const Wrapper = {
components: { Icon },
template: `<Icon :icon="icon" color="purple" style="color: green;" />`,
data() {
return {
icon: iconData,
};
},
};
const wrapper = mount(Wrapper, {});
expect(wrapper.html()).toContain('style="color: green;"');
});
test('attributes that cannot change', () => {
const Wrapper = {
components: { Icon },
template: `<Icon :icon="icon" viewBox="0 0 0 0" preserveAspectRatio="none" />`,
data() {
return {
icon: iconData,
};
},
};
const wrapper = mount(Wrapper, {});
const html = wrapper.html();
expect(html).not.toContain('viewBox="0 0 0 0"');
expect(html).not.toContain('preserveAspectRatio="none"');
});
});

View File

@ -0,0 +1,68 @@
import { mount } from '@vue/test-utils';
import { Icon } from '../../dist/offline';
const iconData = {
body:
'<path d="M4 19h16v2H4zm5-4h11v2H9zm-5-4h16v2H4zm0-8h16v2H4zm5 4h11v2H9z" fill="currentColor"/>',
width: 24,
height: 24,
};
describe('Dimensions', () => {
test('height', () => {
const Wrapper = {
components: { Icon },
template: `<Icon :icon="icon" height="48" />`,
data() {
return {
icon: iconData,
};
},
};
const wrapper = mount(Wrapper, {});
const html = wrapper.html();
expect(html).toContain('height="48"');
expect(html).toContain('width="48"');
expect(html).not.toContain('height="1em"');
expect(html).not.toContain('width="1em"');
});
test('width and height', () => {
const Wrapper = {
components: { Icon },
template: `<Icon :icon="icon" :width="32" height="48" />`,
data() {
return {
icon: iconData,
};
},
};
const wrapper = mount(Wrapper, {});
const html = wrapper.html();
expect(html).toContain('height="48"');
expect(html).toContain('width="32"');
expect(html).not.toContain('height="1em"');
expect(html).not.toContain('width="1em"');
});
test('auto', () => {
const Wrapper = {
components: { Icon },
template: `<Icon :icon="icon" height="auto" />`,
data() {
return {
icon: iconData,
};
},
};
const wrapper = mount(Wrapper, {});
const html = wrapper.html();
expect(html).toContain('height="24"');
expect(html).toContain('width="24"');
expect(html).not.toContain('height="1em"');
expect(html).not.toContain('width="1em"');
});
});

View File

@ -0,0 +1,41 @@
import { mount } from '@vue/test-utils';
import { Icon } from '../../dist/offline';
const iconDataWithID = {
body:
'<defs><path id="ssvg-id-1st-place-medala" d="M.93.01h120.55v58.36H.93z"/><path id="ssvg-id-1st-place-medald" d="M.93.01h120.55v58.36H.93z"/><path id="ssvg-id-1st-place-medalf" d="M.93.01h120.55v58.36H.93z"/><path id="ssvg-id-1st-place-medalh" d="M.93.01h120.55v58.36H.93z"/><path id="ssvg-id-1st-place-medalj" d="M.93.01h120.55v58.36H.93z"/><path id="ssvg-id-1st-place-medalm" d="M.93.01h120.55v58.36H.93z"/><path d="M52.849 78.373v-3.908c3.681-.359 6.25-.958 7.703-1.798c1.454-.84 2.54-2.828 3.257-5.962h4.021v40.385h-5.437V78.373h-9.544z" id="ssvg-id-1st-place-medalp"/><linearGradient x1="49.998%" y1="-13.249%" x2="49.998%" y2="90.002%" id="ssvg-id-1st-place-medalb"><stop stop-color="#1E88E5" offset="13.55%"/><stop stop-color="#1565C0" offset="93.8%"/></linearGradient><linearGradient x1="26.648%" y1="2.735%" x2="77.654%" y2="105.978%" id="ssvg-id-1st-place-medalk"><stop stop-color="#64B5F6" offset="13.55%"/><stop stop-color="#2196F3" offset="94.62%"/></linearGradient><radialGradient cx="22.368%" cy="12.5%" fx="22.368%" fy="12.5%" r="95.496%" id="ssvg-id-1st-place-medalo"><stop stop-color="#FFEB3B" offset="29.72%"/><stop stop-color="#FBC02D" offset="95.44%"/></radialGradient></defs><g fill="none" fill-rule="evenodd"><g transform="translate(3 4)"><mask id="ssvg-id-1st-place-medalc" fill="#fff"><use xlink:href="#ssvg-id-1st-place-medala"/></mask><path fill="url(#ssvg-id-1st-place-medalb)" fill-rule="nonzero" mask="url(#ssvg-id-1st-place-medalc)" d="M45.44 42.18h31.43l30-48.43H75.44z"/></g><g transform="translate(3 4)"><mask id="ssvg-id-1st-place-medale" fill="#fff"><use xlink:href="#ssvg-id-1st-place-medald"/></mask><g opacity=".2" mask="url(#ssvg-id-1st-place-medale)" fill="#424242" fill-rule="nonzero"><path d="M101.23-3L75.2 39H50.85L77.11-3h24.12zm5.64-3H75.44l-30 48h31.42l30.01-48z"/></g></g><g transform="translate(3 4)"><mask id="ssvg-id-1st-place-medalg" fill="#fff"><use xlink:href="#ssvg-id-1st-place-medalf"/></mask><path d="M79 30H43c-4.42 0-8 3.58-8 8v16.04c0 2.17 1.8 3.95 4.02 3.96h.01c2.23-.01 4.97-1.75 4.97-3.96V44c0-1.1.9-2 2-2h30c1.1 0 2 .9 2 2v9.93c0 1.98 2.35 3.68 4.22 4.04c.26.05.52.08.78.08c2.21 0 4-1.79 4-4V38c0-4.42-3.58-8-8-8z" fill="#FDD835" fill-rule="nonzero" mask="url(#ssvg-id-1st-place-medalg)"/></g><g transform="translate(3 4)"><mask id="ssvg-id-1st-place-medali" fill="#fff"><use xlink:href="#ssvg-id-1st-place-medalh"/></mask><g opacity=".2" mask="url(#ssvg-id-1st-place-medali)" fill="#424242" fill-rule="nonzero"><path d="M79 32c3.31 0 6 2.69 6 6v16.04A2.006 2.006 0 0 1 82.59 56c-1.18-.23-2.59-1.35-2.59-2.07V44c0-2.21-1.79-4-4-4H46c-2.21 0-4 1.79-4 4v10.04c0 .88-1.64 1.96-2.97 1.96c-1.12-.01-2.03-.89-2.03-1.96V38c0-3.31 2.69-6 6-6h36zm0-2H43c-4.42 0-8 3.58-8 8v16.04c0 2.17 1.8 3.95 4.02 3.96h.01c2.23-.01 4.97-1.75 4.97-3.96V44c0-1.1.9-2 2-2h30c1.1 0 2 .9 2 2v9.93c0 1.98 2.35 3.68 4.22 4.04c.26.05.52.08.78.08c2.21 0 4-1.79 4-4V38c0-4.42-3.58-8-8-8z"/></g></g><g transform="translate(3 4)"><mask id="ssvg-id-1st-place-medall" fill="#fff"><use xlink:href="#ssvg-id-1st-place-medalj"/></mask><path fill="url(#ssvg-id-1st-place-medalk)" fill-rule="nonzero" mask="url(#ssvg-id-1st-place-medall)" d="M76.87 42.18H45.44l-30-48.43h31.43z"/></g><g transform="translate(3 4)"><mask id="ssvg-id-1st-place-medaln" fill="#fff"><use xlink:href="#ssvg-id-1st-place-medalm"/></mask><g opacity=".2" mask="url(#ssvg-id-1st-place-medaln)" fill="#424242" fill-rule="nonzero"><path d="M45.1-3l26.35 42H47.1L20.86-3H45.1zm1.77-3H15.44l30 48h31.42L46.87-6z"/></g></g><circle fill="url(#ssvg-id-1st-place-medalo)" fill-rule="nonzero" cx="64" cy="86" r="38"/><path d="M64 51c19.3 0 35 15.7 35 35s-15.7 35-35 35s-35-15.7-35-35s15.7-35 35-35zm0-3c-20.99 0-38 17.01-38 38s17.01 38 38 38s38-17.01 38-38s-17.01-38-38-38z" opacity=".2" fill="#424242" fill-rule="nonzero"/><path d="M47.3 63.59h33.4v44.4H47.3z"/><use fill="#000" xlink:href="#ssvg-id-1st-place-medalp"/><use fill="#FFA000" xlink:href="#ssvg-id-1st-place-medalp"/></g>',
width: 128,
height: 128,
};
describe('Replacing IDs', () => {
test('default behavior', () => {
const Wrapper = {
components: { Icon },
template: `<Icon :icon="icon" />`,
data() {
return {
icon: iconDataWithID,
};
},
};
const wrapper = mount(Wrapper, {});
expect(wrapper.html()).not.toContain('ssvg-id-1st-place-medala');
});
test('custom generator', () => {
const Wrapper = {
components: { Icon },
template: `<Icon :icon="icon" id="test" />`,
data() {
return {
icon: iconDataWithID,
};
},
};
const wrapper = mount(Wrapper, {});
expect(wrapper.html()).toContain('id="test-0"');
});
});

View File

@ -0,0 +1,135 @@
import { mount } from '@vue/test-utils';
import { Icon } from '../../dist/offline';
const iconData = {
body:
'<path d="M4 19h16v2H4zm5-4h11v2H9zm-5-4h16v2H4zm0-8h16v2H4zm5 4h11v2H9z" fill="currentColor"/>',
width: 24,
height: 24,
};
describe('Inline attribute', () => {
test('string', () => {
const Wrapper = {
components: { Icon },
template: `<Icon :icon="icon" inline="true" />`,
data() {
return {
icon: iconData,
};
},
};
const wrapper = mount(Wrapper, {});
expect(wrapper.html()).toContain('style="vertical-align: -0.125em;"');
});
test('false string', () => {
// "false" = true
const Wrapper = {
components: { Icon },
template: `<Icon :icon="icon" inline="false" />`,
data() {
return {
icon: iconData,
};
},
};
const wrapper = mount(Wrapper, {});
expect(wrapper.html()).toContain('style="vertical-align: -0.125em;"');
});
test('true', () => {
const Wrapper = {
components: { Icon },
template: `<Icon :icon="icon" :inline="true" />`,
data() {
return {
icon: iconData,
};
},
};
const wrapper = mount(Wrapper, {});
expect(wrapper.html()).toContain('style="vertical-align: -0.125em;"');
});
test('false', () => {
const Wrapper = {
components: { Icon },
template: `<Icon :icon="icon" :inline="false" />`,
data() {
return {
icon: iconData,
};
},
};
const wrapper = mount(Wrapper, {});
expect(wrapper.html()).not.toContain(
'style="vertical-align: -0.125em;"'
);
});
test('inline and style string', () => {
// Style goes after vertical-align
const Wrapper = {
components: { Icon },
template: `<Icon :icon="icon" :inline="true" style="color: red;" />`,
data() {
return {
icon: iconData,
};
},
};
const wrapper = mount(Wrapper, {});
expect(wrapper.html()).toContain(
'vertical-align: -0.125em; color: red;'
);
});
test('inline and style object', () => {
// Style goes after vertical-align
const Wrapper = {
components: { Icon },
template: `<Icon :icon="icon" :inline="true" :style="style" />`,
data() {
return {
icon: iconData,
style: {
color: 'red',
},
};
},
};
const wrapper = mount(Wrapper, {});
expect(wrapper.html()).toContain(
'vertical-align: -0.125em; color: red;'
);
});
test('inline and style overriding it', () => {
// Style goes after vertical-align
const Wrapper = {
components: { Icon },
template: `<Icon :icon="icon" :inline="true" :style="style" />`,
data() {
return {
icon: iconData,
style: {
verticalAlign: 0,
},
};
},
};
const wrapper = mount(Wrapper, {});
// For some reason style is duplicated
expect(wrapper.html()).toContain(
'style="vertical-align: 0; vertical-align: 0;"'
);
});
});

View File

@ -0,0 +1,100 @@
import { mount } from '@vue/test-utils';
import { Icon, addIcon, addCollection } from '../../dist/offline';
const iconData = {
body:
'<path d="M4 19h16v2H4zm5-4h11v2H9zm-5-4h16v2H4zm0-8h16v2H4zm5 4h11v2H9z" fill="currentColor"/>',
width: 24,
height: 24,
};
describe('Using storage', () => {
test('using storage', () => {
addIcon('test-icon', iconData);
const Wrapper = {
components: { Icon },
template: `<Icon icon="test-icon" />`,
};
const wrapper = mount(Wrapper, {});
expect(wrapper.html().replace(/\s*\n\s*/g, '')).toEqual(
'<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" aria-hidden="true" role="img" width="1em" height="1em" preserveAspectRatio="xMidYMid meet" viewBox="0 0 24 24"><path d="M4 19h16v2H4zm5-4h11v2H9zm-5-4h16v2H4zm0-8h16v2H4zm5 4h11v2H9z" fill="currentColor"></path></svg>'
);
});
test('using storage with icon set', () => {
const iconSet = {
prefix: 'mdi-light',
icons: {
account: {
body:
'<path d="M11.5 14c4.142 0 7.5 1.567 7.5 3.5V20H4v-2.5c0-1.933 3.358-3.5 7.5-3.5zm6.5 3.5c0-1.38-2.91-2.5-6.5-2.5S5 16.12 5 17.5V19h13v-1.5zM11.5 5a3.5 3.5 0 1 1 0 7a3.5 3.5 0 0 1 0-7zm0 1a2.5 2.5 0 1 0 0 5a2.5 2.5 0 0 0 0-5z" fill="currentColor"/>',
},
home: {
body:
'<path d="M16 8.414l-4.5-4.5L4.414 11H6v8h3v-6h5v6h3v-8h1.586L17 9.414V6h-1v2.414zM2 12l9.5-9.5L15 6V5h3v4l3 3h-3v7.998h-5v-6h-3v6H5V12H2z" fill="currentColor"/>',
},
},
width: 24,
height: 24,
};
addCollection(iconSet);
const Wrapper = {
components: { Icon },
template: `<Icon :icon="icon" />`,
data() {
return {
icon: 'mdi-light:account',
};
},
};
const wrapper = mount(Wrapper, {});
expect(wrapper.html().replace(/\s*\n\s*/g, '')).toEqual(
'<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" aria-hidden="true" role="img" width="1em" height="1em" preserveAspectRatio="xMidYMid meet" viewBox="0 0 24 24"><path d="M11.5 14c4.142 0 7.5 1.567 7.5 3.5V20H4v-2.5c0-1.933 3.358-3.5 7.5-3.5zm6.5 3.5c0-1.38-2.91-2.5-6.5-2.5S5 16.12 5 17.5V19h13v-1.5zM11.5 5a3.5 3.5 0 1 1 0 7a3.5 3.5 0 0 1 0-7zm0 1a2.5 2.5 0 1 0 0 5a2.5 2.5 0 0 0 0-5z" fill="currentColor"></path></svg>'
);
});
test('using storage with icon set with custom prefix', () => {
const iconSet = {
prefix: 'mdi-light',
icons: {
'account-alert': {
body:
'<path d="M10.5 14c4.142 0 7.5 1.567 7.5 3.5V20H3v-2.5c0-1.933 3.358-3.5 7.5-3.5zm6.5 3.5c0-1.38-2.91-2.5-6.5-2.5S4 16.12 4 17.5V19h13v-1.5zM10.5 5a3.5 3.5 0 1 1 0 7a3.5 3.5 0 0 1 0-7zm0 1a2.5 2.5 0 1 0 0 5a2.5 2.5 0 0 0 0-5zM20 16v-1h1v1h-1zm0-3V7h1v6h-1z" fill="currentColor"/>',
},
'link': {
body:
'<path d="M8 13v-1h7v1H8zm7.5-6a5.5 5.5 0 1 1 0 11H13v-1h2.5a4.5 4.5 0 1 0 0-9H13V7h2.5zm-8 11a5.5 5.5 0 1 1 0-11H10v1H7.5a4.5 4.5 0 1 0 0 9H10v1H7.5z" fill="currentColor"/>',
},
},
width: 24,
height: 24,
};
addCollection(iconSet, 'custom-');
const Wrapper = {
components: { Icon },
template: `<Icon icon="custom-link" />`,
};
const wrapper = mount(Wrapper, {});
expect(wrapper.html().replace(/\s*\n\s*/g, '')).toEqual(
'<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" aria-hidden="true" role="img" width="1em" height="1em" preserveAspectRatio="xMidYMid meet" viewBox="0 0 24 24"><path d="M8 13v-1h7v1H8zm7.5-6a5.5 5.5 0 1 1 0 11H13v-1h2.5a4.5 4.5 0 1 0 0-9H13V7h2.5zm-8 11a5.5 5.5 0 1 1 0-11H10v1H7.5a4.5 4.5 0 1 0 0 9H10v1H7.5z" fill="currentColor"></path></svg>'
);
});
test('missing icon from storage', () => {
const Wrapper = {
components: { Icon },
template: `<Icon icon="missing-icon" />`,
};
const wrapper = mount(Wrapper, {});
expect(wrapper.html().replace(/\s*\n\s*/g, '')).toEqual('');
});
});

View File

@ -0,0 +1,208 @@
import { mount } from '@vue/test-utils';
import { Icon } from '../../dist/offline';
const iconData = {
body:
'<path d="M4 19h16v2H4zm5-4h11v2H9zm-5-4h16v2H4zm0-8h16v2H4zm5 4h11v2H9z" fill="currentColor"/>',
width: 24,
height: 24,
};
describe('Rotation', () => {
test('number', () => {
const Wrapper = {
components: { Icon },
template: `<Icon :icon="icon" :rotate="1" />`,
data() {
return {
icon: iconData,
};
},
};
const wrapper = mount(Wrapper, {});
expect(wrapper.html().replace(/\s*\n\s*/g, '')).toEqual(
'<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" aria-hidden="true" role="img" width="1em" height="1em" preserveAspectRatio="xMidYMid meet" viewBox="0 0 24 24"><g transform="rotate(90 12 12)"><path d="M4 19h16v2H4zm5-4h11v2H9zm-5-4h16v2H4zm0-8h16v2H4zm5 4h11v2H9z" fill="currentColor"></path></g></svg>'
);
});
test('string', () => {
const Wrapper = {
components: { Icon },
template: `<Icon :icon="icon" rotate="180deg" />`,
data() {
return {
icon: iconData,
};
},
};
const wrapper = mount(Wrapper, {});
expect(wrapper.html()).toContain('<g transform="rotate(180 12 12)">');
});
});
describe('Flip', () => {
test('boolean', () => {
const Wrapper = {
components: { Icon },
template: `<Icon :icon="icon" :hFlip="true" />`,
data() {
return {
icon: iconData,
};
},
};
const wrapper = mount(Wrapper, {});
expect(wrapper.html()).toContain(
'<g transform="translate(24 0) scale(-1 1)">'
);
});
test('string', () => {
const Wrapper = {
components: { Icon },
template: `<Icon :icon="icon" flip="vertical" />`,
data() {
return {
icon: iconData,
};
},
};
const wrapper = mount(Wrapper, {});
expect(wrapper.html()).toContain(
'<g transform="translate(0 24) scale(1 -1)">'
);
});
test('string and boolean', () => {
const Wrapper = {
components: { Icon },
template: `<Icon :icon="icon" flip="horizontal" :vFlip="true" />`,
data() {
return {
icon: iconData,
};
},
};
const wrapper = mount(Wrapper, {});
expect(wrapper.html()).toContain('<g transform="rotate(180 12 12)">');
});
test('string for boolean attribute', () => {
const Wrapper = {
components: { Icon },
template: `<Icon :icon="icon" horizontal-flip="true" />`,
data() {
return {
icon: iconData,
};
},
};
const wrapper = mount(Wrapper, {});
expect(wrapper.html()).toContain(
'<g transform="translate(24 0) scale(-1 1)">'
);
});
test('shorthand and boolean', () => {
// 'flip' is processed after 'hFlip', overwriting value
const Wrapper = {
components: { Icon },
template: `<Icon :icon="icon" flip="horizontal" :hFlip="false" />`,
data() {
return {
icon: iconData,
};
},
};
const wrapper = mount(Wrapper, {});
expect(wrapper.html()).toContain(
'<g transform="translate(24 0) scale(-1 1)">'
);
});
test('shorthand and boolean as string', () => {
const Wrapper = {
components: { Icon },
template: `<Icon :icon="icon" flip="vertical" :horizontal-flip="true" />`,
data() {
return {
icon: iconData,
};
},
};
const wrapper = mount(Wrapper, {});
expect(wrapper.html()).toContain('<g transform="rotate(180 12 12)">');
});
test('wrong case', () => {
const Wrapper = {
components: { Icon },
template: `<Icon :icon="icon" :vflip="true" />`,
data() {
return {
icon: iconData,
};
},
};
const wrapper = mount(Wrapper, {});
expect(wrapper.html()).not.toContain('<g transform="');
});
});
describe('Alignment and slice', () => {
test('vAlign and slice', () => {
const Wrapper = {
components: { Icon },
template: `<Icon :icon="icon" vAlign="top" :slice="true" />`,
data() {
return {
icon: iconData,
};
},
};
const wrapper = mount(Wrapper, {});
expect(wrapper.html()).toContain(
'preserveAspectRatio="xMidYMin slice"'
);
});
test('string', () => {
const Wrapper = {
components: { Icon },
template: `<Icon :icon="icon" align="left bottom" />`,
data() {
return {
icon: iconData,
};
},
};
const wrapper = mount(Wrapper, {});
expect(wrapper.html()).toContain('preserveAspectRatio="xMinYMax meet"');
});
test('aliases', () => {
const Wrapper = {
components: { Icon },
template: `<Icon :icon="icon" h-align="left" vertical-align="bottom" />`,
data() {
return {
icon: iconData,
};
},
};
const wrapper = mount(Wrapper, {});
expect(wrapper.html()).toContain('preserveAspectRatio="xMinYMax meet"');
});
});

View File

@ -2,7 +2,7 @@
"compilerOptions": {
"rootDir": "./src",
"outDir": "./lib",
"target": "ES2019",
"target": "ESNext",
"module": "ESNext",
"declaration": true,
"sourceMap": false,