2
0
mirror of https://github.com/iconify/iconify.git synced 2024-12-12 05:37:49 +00:00

chore: migrate react tests to vitest, publish react component update

This commit is contained in:
Vjacheslav Trushkin 2024-07-25 21:55:00 +03:00
parent ad3ffb793f
commit 17a46f6a75
45 changed files with 2013 additions and 2111 deletions

View File

@ -1,3 +0,0 @@
{
"presets": ["@babel/preset-env", "@babel/preset-react"]
}

View File

@ -7,7 +7,9 @@
"@cyberalien/redundancy",
"@iconify/api-redundancy"
],
"compiler": {},
"compiler": {
"tsconfigFilePath": "<projectFolder>/tsconfig.src.json"
},
"apiReport": {
"enabled": false
},

View File

@ -3,7 +3,7 @@
"description": "Iconify icon component for React.",
"author": "Vjacheslav Trushkin",
"type": "module",
"version": "5.0.1",
"version": "5.0.2",
"license": "MIT",
"bugs": "https://github.com/iconify/iconify/issues",
"homepage": "https://iconify.design/",
@ -17,12 +17,12 @@
"clean": "rimraf lib dist tsconfig.tsbuildinfo",
"prebuild": "pnpm run clean",
"build": "node build",
"build:lib": "tsc -b",
"build:lib": "tsc -b tsconfig.src.json",
"build:dist": "rollup -c rollup.config.js",
"prebuild:api": "api-extractor run --local --verbose --config api-extractor.offline.json",
"build:api": "api-extractor run --local --verbose --config api-extractor.iconify.json",
"build:cleanup": "node cleanup",
"test": "jest --runInBand"
"test": "vitest"
},
"main": "dist/iconify.js",
"types": "dist/iconify.d.ts",
@ -51,20 +51,20 @@
"@iconify/types": "workspace:^"
},
"devDependencies": {
"@babel/preset-env": "^7.24.8",
"@babel/preset-react": "^7.24.7",
"@iconify/core": "workspace:^",
"@iconify/utils": "workspace:^",
"@microsoft/api-extractor": "^7.47.2",
"@rollup/plugin-node-resolve": "^15.2.3",
"@testing-library/jest-dom": "^6.4.6",
"@testing-library/react": "^16.0.0",
"@types/react": "^18.3.3",
"babel-jest": "^29.7.0",
"jest": "^29.7.0",
"@types/react-dom": "^18.3.0",
"jsdom": "^24.1.1",
"react": "^18.3.1",
"react-test-renderer": "^18.3.1",
"rimraf": "^4.4.1",
"rollup": "^3.29.4",
"typescript": "^5.5.3"
"typescript": "^5.5.3",
"vitest": "^2.0.4"
},
"peerDependencies": {
"react": ">=16"

View File

@ -1,6 +1,7 @@
import { loadIcons, iconExists } from '../../dist/iconify';
import { mockAPIData } from '@iconify/core/lib/api/modules/mock';
import { provider, nextPrefix } from './load';
import { describe, test, expect } from 'vitest';
describe('Testing fake API', () => {
test('using fake API to load icon', () => {

View File

@ -1,8 +1,9 @@
import React from 'react';
import renderer from 'react-test-renderer';
import { Icon, loadIcons, iconExists } from '../../dist/iconify';
import { mockAPIData } from '@iconify/core/lib/api/modules/mock';
import { provider, nextPrefix } from './load';
import { describe, test, expect } from 'vitest';
import { render } from '@testing-library/react';
const iconData = {
body: '<path d="M4 19h16v2H4zm5-4h11v2H9zm-5-4h16v2H4zm0-8h16v2H4zm5 4h11v2H9z" fill="currentColor"/>',
@ -12,11 +13,11 @@ const iconData = {
describe('Rendering icon', () => {
test('rendering icon after loading it', () => {
return new Promise((fulfill) => {
return new Promise((resolve) => {
const prefix = nextPrefix();
const name = 'render-test';
const iconName = `@${provider}:${prefix}:${name}`;
const className = `iconify iconify--${prefix} iconify--${provider}`;
const className = `iconify iconify--${provider} iconify--${prefix}`;
let onLoadCalled = false;
mockAPIData({
@ -49,7 +50,7 @@ describe('Rendering icon', () => {
expect(iconExists(iconName)).toEqual(true);
// Render component
const component = renderer.create(
const renderResult = render(
<Icon
icon={iconName}
onLoad={(name) => {
@ -59,42 +60,24 @@ describe('Rendering icon', () => {
}}
/>
);
const tree = component.toJSON();
expect(tree).toMatchObject({
type: 'svg',
props: {
'xmlns': 'http://www.w3.org/2000/svg',
'xmlnsXlink': 'http://www.w3.org/1999/xlink',
'aria-hidden': true,
'role': 'img',
'style': {},
'dangerouslySetInnerHTML': {
__html: iconData.body,
},
'width': '1em',
'height': '1em',
'viewBox':
'0 0 ' + iconData.width + ' ' + iconData.height,
className,
},
children: null,
});
expect(renderResult.container.innerHTML).toEqual(
`<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" aria-hidden="true" role="img" class="${className}" width="1em" height="1em" viewBox="0 0 24 24"><path d="M4 19h16v2H4zm5-4h11v2H9zm-5-4h16v2H4zm0-8h16v2H4zm5 4h11v2H9z" fill="currentColor"></path></svg>`
);
// Make sure onLoad has been called
expect(onLoadCalled).toEqual(true);
fulfill(true);
resolve(true);
});
});
});
test('rendering icon before loading it', () => {
return new Promise((fulfill) => {
return new Promise((resolve, reject) => {
const prefix = nextPrefix();
const name = 'mock-test';
const iconName = `@${provider}:${prefix}:${name}`;
const className = `iconify iconify--${prefix} iconify--${provider}`;
const className = `iconify iconify--${provider} iconify--${prefix}`;
let onLoadCalled = false;
mockAPIData({
@ -120,42 +103,32 @@ describe('Rendering icon', () => {
// Test it again
expect(iconExists(iconName)).toEqual(true);
// Check if state was changed
// Wrapped in double setTimeout() because re-render takes 2 ticks
setTimeout(() => {
setTimeout(() => {
const tree = component.toJSON();
// Check if state was changed in next few ticks
let counter = 0;
const timer = setInterval(() => {
counter++;
const html = renderResult.container.innerHTML;
if (html.includes('<span')) {
// Not rendered yet
if (counter > 5) {
clearInterval(timer);
reject(new Error('Icon not rendered'));
}
return;
}
expect(tree).toMatchObject({
type: 'svg',
props: {
'xmlns': 'http://www.w3.org/2000/svg',
'xmlnsXlink':
'http://www.w3.org/1999/xlink',
'aria-hidden': true,
'role': 'img',
'style': {},
'dangerouslySetInnerHTML': {
__html: iconData.body,
},
'width': '1em',
'height': '1em',
'viewBox':
'0 0 ' +
iconData.width +
' ' +
iconData.height,
'className': 'test ' + className,
},
children: null,
});
// Should be rendered: test it
clearInterval(timer);
// onLoad should have been called
expect(onLoadCalled).toEqual(true);
expect(html).toEqual(
`<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" aria-hidden="true" role="img" class="${className} test" width="1em" height="1em" viewBox="0 0 24 24"><path d="M4 19h16v2H4zm5-4h11v2H9zm-5-4h16v2H4zm0-8h16v2H4zm5 4h11v2H9z" fill="currentColor"></path></svg>`
);
fulfill(true);
}, 0);
}, 0);
// onLoad should have been called
expect(onLoadCalled).toEqual(true);
resolve(true);
});
},
});
@ -163,7 +136,7 @@ describe('Rendering icon', () => {
expect(iconExists(iconName)).toEqual(false);
// Render component
const component = renderer.create(
const renderResult = render(
<Icon
icon={iconName}
className="test"
@ -174,14 +147,9 @@ describe('Rendering icon', () => {
}}
/>
);
const tree = component.toJSON();
// Should render placeholder
expect(tree).toMatchObject({
type: 'span',
props: {},
children: null,
});
expect(renderResult.container.innerHTML).toEqual('<span></span>');
// onLoad should not have been called yet
expect(onLoadCalled).toEqual(false);
@ -189,7 +157,7 @@ describe('Rendering icon', () => {
});
test('missing icon', () => {
return new Promise((fulfill) => {
return new Promise((resolve, reject) => {
const prefix = nextPrefix();
const name = 'missing-icon';
const iconName = `@${provider}:${prefix}:${name}`;
@ -208,21 +176,30 @@ describe('Rendering icon', () => {
// Test it again
expect(iconExists(iconName)).toEqual(false);
// Check if state was changed
// Wrapped in double setTimeout() because re-render takes 2 ticks
setTimeout(() => {
setTimeout(() => {
const tree = component.toJSON();
// Check if state was changed in next few ticks
let counter = 0;
const timer = setInterval(() => {
counter++;
const html = renderResult.container.innerHTML;
if (html.includes('<span')) {
// Not rendered yet
if (counter > 5) {
// Success
clearInterval(timer);
resolve(true);
}
return;
}
expect(tree).toMatchObject({
type: 'span',
props: {},
children: null,
});
fulfill(true);
}, 0);
}, 0);
// Content changed???
clearInterval(timer);
reject(
new Error(
'Bad icon content: ' +
renderResult.container.innerHTML
)
);
});
},
});
@ -230,7 +207,7 @@ describe('Rendering icon', () => {
expect(iconExists(iconName)).toEqual(false);
// Render component
const component = renderer.create(
const renderResult = render(
<Icon
icon={iconName}
onLoad={() => {
@ -238,14 +215,9 @@ describe('Rendering icon', () => {
}}
></Icon>
);
const tree = component.toJSON();
// Should render placeholder
expect(tree).toMatchObject({
type: 'span',
props: {},
children: null,
});
expect(renderResult.container.innerHTML).toEqual('<span></span>');
});
});
});

View File

@ -1,414 +0,0 @@
import React from 'react';
import renderer from 'react-test-renderer';
import { Icon, iconExists } from '../../dist/iconify';
import { mockAPIData } from '@iconify/core/lib/api/modules/mock';
import { provider, nextPrefix } from './load';
const iconData = {
body: '<path d="M4 19h16v2H4zm5-4h11v2H9zm-5-4h16v2H4zm0-8h16v2H4zm5 4h11v2H9z" fill="currentColor"/>',
width: 24,
height: 24,
};
const iconData2 = {
body: '<path d="M19.031 4.281l-11 11l-.687.719l.687.719l11 11l1.438-1.438L10.187 16L20.47 5.719z" fill="currentColor"/>',
width: 32,
height: 32,
};
describe('Rendering icon', () => {
test('changing icon property', () => {
return new Promise((fulfill) => {
const prefix = nextPrefix();
const name = 'changing-prop';
const name2 = 'changing-prop2';
const iconName = `@${provider}:${prefix}:${name}`;
const iconName2 = `@${provider}:${prefix}:${name2}`;
const className = `iconify iconify--${prefix} iconify--${provider}`;
let onLoadCalled = ''; // Name of icon from last onLoad call
const onLoad = (name) => {
// onLoad should be called only once per icon
switch (name) {
// First onLoad call
case iconName:
expect(onLoadCalled).toEqual('');
break;
// Second onLoad call
case iconName2:
expect(onLoadCalled).toEqual(iconName);
break;
default:
throw new Error(`Unexpected onLoad('${name}') call`);
}
onLoadCalled = name;
};
mockAPIData({
type: 'icons',
provider,
prefix,
response: {
prefix,
icons: {
[name]: iconData,
},
},
delay: (next) => {
// Icon should not have loaded yet
expect(iconExists(iconName)).toEqual(false);
// onLoad should not have been called yet
expect(onLoadCalled).toEqual('');
// Send icon data
next();
// Test it again
expect(iconExists(iconName)).toEqual(true);
// Check if state was changed
// Wrapped in double setTimeout() because re-render takes 2 ticks
setTimeout(() => {
setTimeout(() => {
const tree = component.toJSON();
expect(tree).toMatchObject({
type: 'svg',
props: {
'xmlns': 'http://www.w3.org/2000/svg',
'xmlnsXlink':
'http://www.w3.org/1999/xlink',
'aria-hidden': true,
'role': 'img',
'style': {},
'dangerouslySetInnerHTML': {
__html: iconData.body,
},
'width': '1em',
'height': '1em',
'viewBox':
'0 0 ' +
iconData.width +
' ' +
iconData.height,
className,
},
children: null,
});
// onLoad should have been called
expect(onLoadCalled).toEqual(iconName);
// Change property
component.update(
<Icon icon={iconName2} onLoad={onLoad} />
);
}, 0);
}, 0);
},
});
mockAPIData({
type: 'icons',
provider,
prefix,
response: {
prefix,
icons: {
[name2]: iconData2,
},
},
delay: (next) => {
// Icon should not have loaded yet
expect(iconExists(iconName2)).toEqual(false);
// Send icon data
next();
// Test it again
expect(iconExists(iconName2)).toEqual(true);
// Check if state was changed
// Wrapped in double setTimeout() because re-render takes 2 ticks
setTimeout(() => {
setTimeout(() => {
const tree = component.toJSON();
expect(tree).toMatchObject({
type: 'svg',
props: {
'xmlns': 'http://www.w3.org/2000/svg',
'xmlnsXlink':
'http://www.w3.org/1999/xlink',
'aria-hidden': true,
'role': 'img',
'style': {},
'dangerouslySetInnerHTML': {
__html: iconData2.body,
},
'width': '1em',
'height': '1em',
'viewBox':
'0 0 ' +
iconData2.width +
' ' +
iconData2.height,
className,
},
children: null,
});
// onLoad should have been called for second icon
expect(onLoadCalled).toEqual(iconName2);
fulfill(true);
}, 0);
}, 0);
},
});
// Check if icon has been loaded
expect(iconExists(iconName)).toEqual(false);
// Render component
const component = renderer.create(
<Icon icon={iconName} onLoad={onLoad} />
);
const tree = component.toJSON();
// Should render placeholder
expect(tree).toMatchObject({
type: 'span',
props: {},
children: null,
});
// onLoad should not have been called yet
expect(onLoadCalled).toEqual('');
});
});
test('changing icon property while loading', () => {
return new Promise((fulfill) => {
const prefix = nextPrefix();
const name = 'changing-prop';
const name2 = 'changing-prop2';
const iconName = `@${provider}:${prefix}:${name}`;
const iconName2 = `@${provider}:${prefix}:${name2}`;
const className = `iconify iconify--${prefix} iconify--${provider}`;
let isSync = true;
mockAPIData({
type: 'icons',
provider,
prefix,
response: {
prefix,
icons: {
[name]: iconData,
},
},
delay: (next) => {
// Should have been called asynchronously, which means icon name has changed
expect(isSync).toEqual(false);
// Send icon data
next();
},
});
mockAPIData({
type: 'icons',
provider,
prefix,
response: {
prefix,
icons: {
[name2]: iconData2,
},
},
delay: (next) => {
// Should have been called asynchronously
expect(isSync).toEqual(false);
// Icon should not have loaded yet
expect(iconExists(iconName2)).toEqual(false);
// Send icon data
next();
// Test it again
expect(iconExists(iconName2)).toEqual(true);
// Check if state was changed
// Wrapped in double setTimeout() because re-render takes 2 ticks
setTimeout(() => {
setTimeout(() => {
const tree = component.toJSON();
expect(tree).toMatchObject({
type: 'svg',
props: {
'xmlns': 'http://www.w3.org/2000/svg',
'xmlnsXlink':
'http://www.w3.org/1999/xlink',
'aria-hidden': true,
'role': 'img',
'style': {},
'dangerouslySetInnerHTML': {
__html: iconData2.body,
},
'width': '1em',
'height': '1em',
'viewBox':
'0 0 ' +
iconData2.width +
' ' +
iconData2.height,
className,
},
children: null,
});
fulfill(true);
}, 0);
}, 0);
},
});
// Check if icon has been loaded
expect(iconExists(iconName)).toEqual(false);
// Render component
const component = renderer.create(<Icon icon={iconName} />);
const tree = component.toJSON();
// Should render placeholder
expect(tree).toMatchObject({
type: 'span',
props: {},
children: null,
});
// Change icon name
component.update(<Icon icon={iconName2} />);
// Async
isSync = false;
});
});
test('changing multiple properties', () => {
return new Promise((fulfill) => {
const prefix = nextPrefix();
const name = 'multiple-props';
const iconName = `@${provider}:${prefix}:${name}`;
const className = `iconify iconify--${prefix} iconify--${provider}`;
mockAPIData({
type: 'icons',
provider,
prefix,
response: {
prefix,
icons: {
[name]: iconData,
},
},
delay: (next) => {
// Icon should not have loaded yet
expect(iconExists(iconName)).toEqual(false);
// Send icon data
next();
// Test it again
expect(iconExists(iconName)).toEqual(true);
// Check if state was changed
// Wrapped in double setTimeout() because re-render takes 2 ticks
setTimeout(() => {
setTimeout(() => {
let tree = component.toJSON();
expect(tree).toMatchObject({
type: 'svg',
props: {
'xmlns': 'http://www.w3.org/2000/svg',
'xmlnsXlink':
'http://www.w3.org/1999/xlink',
'aria-hidden': true,
'role': 'img',
'style': {},
'dangerouslySetInnerHTML': {
__html: iconData.body,
},
'width': '1em',
'height': '1em',
'viewBox':
'0 0 ' +
iconData.width +
' ' +
iconData.height,
className,
},
children: null,
});
// Add horizontal flip and style
component.update(
<Icon
icon={iconName}
hFlip={true}
style={{ color: 'red' }}
/>
);
tree = component.toJSON();
expect(tree).toMatchObject({
type: 'svg',
props: {
'xmlns': 'http://www.w3.org/2000/svg',
'xmlnsXlink':
'http://www.w3.org/1999/xlink',
'aria-hidden': true,
'role': 'img',
'style': {
color: 'red',
},
'dangerouslySetInnerHTML': {
__html: `<g transform="translate(${iconData.width} 0) scale(-1 1)">${iconData.body}</g>`,
},
'width': '1em',
'height': '1em',
'viewBox':
'0 0 ' +
iconData.width +
' ' +
iconData.height,
className,
},
children: null,
});
fulfill(true);
}, 0);
}, 0);
},
});
// Check if icon has been loaded
expect(iconExists(iconName)).toEqual(false);
// Render component with placeholder text
const component = renderer.create(
<Icon icon={iconName}>loading...</Icon>
);
const tree = component.toJSON();
// Should render placeholder
expect(tree).toEqual('loading...');
});
});
});

View File

@ -0,0 +1,344 @@
import React from 'react';
import { Icon, iconExists } from '../../dist/iconify';
import { mockAPIData } from '@iconify/core/lib/api/modules/mock';
import { provider, nextPrefix } from './load';
import { describe, test, expect } from 'vitest';
import { render } from '@testing-library/react';
const path1 = 'M4 19h16v2H4zm5-4h11v2H9zm-5-4h16v2H4zm0-8h16v2H4zm5 4h11v2H9z';
const iconData = {
body: `<path d="${path1}" fill="currentColor"/>`,
width: 24,
height: 24,
};
const path2 =
'M19.031 4.281l-11 11l-.687.719l.687.719l11 11l1.438-1.438L10.187 16L20.47 5.719z';
const iconData2 = {
body: `<path d="${path2}" fill="currentColor"/>`,
width: 32,
height: 32,
};
describe('Rendering icon', () => {
test('changing icon property', () => {
return new Promise((resolve, reject) => {
const prefix = nextPrefix();
const name = 'changing-prop';
const name2 = 'changing-prop2';
const iconName = `@${provider}:${prefix}:${name}`;
const iconName2 = `@${provider}:${prefix}:${name2}`;
let onLoadCalled = ''; // Name of icon from last onLoad call
const onLoad = (name) => {
// onLoad should be called only once per icon
switch (name) {
// First onLoad call
case iconName:
expect(onLoadCalled).toEqual('');
break;
// Second onLoad call
case iconName2:
expect(onLoadCalled).toEqual(iconName);
break;
default:
throw new Error(`Unexpected onLoad('${name}') call`);
}
onLoadCalled = name;
};
mockAPIData({
type: 'icons',
provider,
prefix,
response: {
prefix,
icons: {
[name]: iconData,
},
},
delay: (next) => {
// Icon should not have loaded yet
expect(iconExists(iconName)).toEqual(false);
// onLoad should not have been called yet
expect(onLoadCalled).toEqual('');
// Send icon data
next();
// Test it again
expect(iconExists(iconName)).toEqual(true);
// Check if state was changed in next few ticks
let counter = 0;
const timer = setInterval(() => {
counter++;
const html = renderResult.container.innerHTML;
if (html.includes('<span')) {
// Not rendered yet
if (counter > 5) {
clearInterval(timer);
reject(new Error('Icon not rendered'));
}
return;
}
// Should be rendered: test it
clearInterval(timer);
expect(html).toContain(path1);
// onLoad should have been called
expect(onLoadCalled).toEqual(iconName);
// Change property
renderResult.rerender(
<Icon icon={iconName2} onLoad={onLoad} />
);
});
},
});
mockAPIData({
type: 'icons',
provider,
prefix,
response: {
prefix,
icons: {
[name2]: iconData2,
},
},
delay: (next) => {
// Icon should not have loaded yet
expect(iconExists(iconName2)).toEqual(false);
// Send icon data
next();
// Test it again
expect(iconExists(iconName2)).toEqual(true);
// Check if state was changed in next few ticks
let counter = 0;
const timer = setInterval(() => {
counter++;
const html = renderResult.container.innerHTML;
if (html.includes('<span')) {
// Not rendered yet
if (counter > 5) {
clearInterval(timer);
reject(new Error('Icon was not updated'));
}
return;
}
// Should be rendered: test it
clearInterval(timer);
expect(html).toContain(path2);
// onLoad should have been called for second icon
expect(onLoadCalled).toEqual(iconName2);
resolve(true);
});
},
});
// Check if icon has been loaded
expect(iconExists(iconName)).toEqual(false);
// Render component
const renderResult = render(
<Icon icon={iconName} onLoad={onLoad} />
);
// Should render placeholder
expect(renderResult.container.innerHTML).toEqual('<span></span>');
// onLoad should not have been called yet
expect(onLoadCalled).toEqual('');
});
});
test('changing icon property while loading', () => {
return new Promise((resolve, reject) => {
const prefix = nextPrefix();
const name = 'changing-prop';
const name2 = 'changing-prop2';
const iconName = `@${provider}:${prefix}:${name}`;
const iconName2 = `@${provider}:${prefix}:${name2}`;
let isSync = true;
mockAPIData({
type: 'icons',
provider,
prefix,
response: {
prefix,
icons: {
[name]: iconData,
},
},
delay: (next) => {
// Should have been called asynchronously, which means icon name has changed
expect(isSync).toEqual(false);
// Send icon data
next();
},
});
mockAPIData({
type: 'icons',
provider,
prefix,
response: {
prefix,
icons: {
[name2]: iconData2,
},
},
delay: (next) => {
// Should have been called asynchronously
expect(isSync).toEqual(false);
// Icon should not have loaded yet
expect(iconExists(iconName2)).toEqual(false);
// Send icon data
next();
// Test it again
expect(iconExists(iconName2)).toEqual(true);
// Check if state was changed in next few ticks
let counter = 0;
const timer = setInterval(() => {
counter++;
const html = renderResult.container.innerHTML;
if (html.includes('<span')) {
// Not rendered yet
if (counter > 5) {
clearInterval(timer);
reject(new Error('Icon not rendered'));
}
return;
}
// Should be rendered: test it
clearInterval(timer);
expect(html).toContain(path2);
resolve(true);
});
},
});
// Check if icon has been loaded
expect(iconExists(iconName)).toEqual(false);
// Render component
const renderResult = render(<Icon icon={iconName} />);
// Should render placeholder
expect(renderResult.container.innerHTML).toEqual('<span></span>');
// Change icon name
renderResult.rerender(<Icon icon={iconName2} />);
// Async
isSync = false;
});
});
test('changing multiple properties', () => {
return new Promise((resolve, reject) => {
const prefix = nextPrefix();
const name = 'multiple-props';
const iconName = `@${provider}:${prefix}:${name}`;
const className = `iconify iconify--${prefix} iconify--${provider}`;
mockAPIData({
type: 'icons',
provider,
prefix,
response: {
prefix,
icons: {
[name]: iconData,
},
},
delay: (next) => {
// Icon should not have loaded yet
expect(iconExists(iconName)).toEqual(false);
// Send icon data
next();
// Test it again
expect(iconExists(iconName)).toEqual(true);
// Check if state was changed in next few ticks
let counter = 0;
const timer = setInterval(() => {
counter++;
const html = renderResult.container.innerHTML;
if (!html.includes('<svg')) {
// Not rendered yet
if (counter > 5) {
clearInterval(timer);
reject(new Error('Icon not rendered'));
}
return;
}
// Should be rendered: test it
clearInterval(timer);
expect(html).toContain(path1);
expect(html).not.toContain(
`<g transform="translate(${iconData.width} 0) scale(-1 1)">`
);
expect(html).not.toContain('style="color: red;"');
// Add horizontal flip and style
renderResult.rerender(
<Icon
icon={iconName}
hFlip={true}
style={{ color: 'red' }}
/>
);
// Should be updated immediately
expect(renderResult.container.innerHTML).toContain(
`<g transform="translate(${iconData.width} 0) scale(-1 1)">`
);
expect(renderResult.container.innerHTML).toContain(
'style="color: red;"'
);
resolve(true);
});
},
});
// Check if icon has been loaded
expect(iconExists(iconName)).toEqual(false);
// Render component with placeholder text
const renderResult = render(
<Icon icon={iconName}>loading...</Icon>
);
// Should render placeholder
expect(renderResult.container.innerHTML).toEqual('loading...');
});
});
});

View File

@ -1,8 +1,9 @@
import React from 'react';
import renderer from 'react-test-renderer';
import { Icon, InlineIcon, loadIcons, iconExists } from '../../dist/iconify';
import { mockAPIData } from '@iconify/core/lib/api/modules/mock';
import { provider, nextPrefix } from './load';
import { describe, test, expect } from 'vitest';
import { render } from '@testing-library/react';
const iconData = {
body: '<path d="M4 19h16v2H4zm5-4h11v2H9zm-5-4h16v2H4zm0-8h16v2H4zm5 4h11v2H9z" fill="currentColor"/>',
@ -12,7 +13,7 @@ const iconData = {
describe('Testing references', () => {
test('reference for preloaded icon', () => {
return new Promise((fulfill) => {
return new Promise((resolve) => {
const prefix = nextPrefix();
const name = 'render-test';
const iconName = `@${provider}:${prefix}:${name}`;
@ -50,7 +51,7 @@ describe('Testing references', () => {
expect(iconExists(iconName)).toEqual(true);
// Render components
renderer.create(
render(
<Icon
icon={iconName}
ref={(element) => {
@ -59,7 +60,7 @@ describe('Testing references', () => {
/>
);
renderer.create(
render(
<InlineIcon
icon={iconName}
ref={(element) => {
@ -72,13 +73,13 @@ describe('Testing references', () => {
expect(gotRef).toEqual(true);
expect(gotInlineRef).toEqual(true);
fulfill(true);
resolve(true);
});
});
});
test('reference to pending icon', () => {
return new Promise((fulfill) => {
return new Promise((resolve, reject) => {
const prefix = nextPrefix();
const name = 'mock-test';
const iconName = `@${provider}:${prefix}:${name}`;
@ -108,15 +109,23 @@ describe('Testing references', () => {
expect(iconExists(iconName)).toEqual(true);
expect(gotRef).toEqual(false);
// Check if state was changed
// Wrapped in double setTimeout() because re-render takes 2 ticks
setTimeout(() => {
setTimeout(() => {
expect(gotRef).toEqual(true);
// Check if state was changed in next few ticks
let counter = 0;
const timer = setInterval(() => {
counter++;
if (!gotRef) {
// Test again
if (counter > 5) {
clearInterval(timer);
reject(new Error('Icon reference was not set'));
}
return;
}
fulfill(true);
}, 0);
}, 0);
// Success!
clearInterval(timer);
resolve(true);
});
},
});
@ -124,7 +133,7 @@ describe('Testing references', () => {
expect(iconExists(iconName)).toEqual(false);
// Render component
renderer.create(
render(
<Icon
icon={iconName}
ref={(element) => {
@ -139,7 +148,7 @@ describe('Testing references', () => {
});
test('missing icon', () => {
return new Promise((fulfill) => {
return new Promise((resolve, reject) => {
const prefix = nextPrefix();
const name = 'missing-icon';
const iconName = `@${provider}:${prefix}:${name}`;
@ -164,16 +173,23 @@ describe('Testing references', () => {
expect(iconExists(iconName)).toEqual(false);
expect(gotRef).toEqual(false);
// Check if state was changed
// Wrapped in double setTimeout() because re-render takes 2 ticks
setTimeout(() => {
setTimeout(() => {
// Reference should not have been called
expect(gotRef).toEqual(false);
// Check if state was changed in next few ticks
let counter = 0;
const timer = setInterval(() => {
counter++;
if (gotRef) {
// Failed!
clearInterval(timer);
reject(new Error('Icon reference was set'));
return;
}
fulfill(true);
}, 0);
}, 0);
if (counter > 5) {
// Waited enough. Success
clearInterval(timer);
resolve(true);
}
});
},
});
@ -181,7 +197,7 @@ describe('Testing references', () => {
expect(iconExists(iconName)).toEqual(false);
// Render component
const component = renderer.create(
render(
<Icon
icon={iconName}
ref={(element) => {

View File

@ -1,67 +0,0 @@
import React from 'react';
import { Icon, InlineIcon } from '../../dist/iconify';
import renderer from 'react-test-renderer';
const iconData = {
body: '<path d="M4 19h16v2H4zm5-4h11v2H9zm-5-4h16v2H4zm0-8h16v2H4zm5 4h11v2H9z" fill="currentColor"/>',
width: 24,
height: 24,
};
describe('Creating component using object', () => {
test('basic icon', () => {
const component = renderer.create(
<Icon
icon={iconData}
onLoad={() => {
// Should be called only for icons loaded from API
throw new Error('onLoad called for object!');
}}
/>
);
const tree = component.toJSON();
expect(tree).toMatchObject({
type: 'svg',
props: {
'xmlns': 'http://www.w3.org/2000/svg',
'xmlnsXlink': 'http://www.w3.org/1999/xlink',
'aria-hidden': true,
'role': 'img',
'style': {},
'dangerouslySetInnerHTML': {
__html: iconData.body,
},
'width': '1em',
'height': '1em',
'viewBox': '0 0 ' + iconData.width + ' ' + iconData.height,
},
children: null,
});
});
test('inline icon', () => {
const component = renderer.create(<InlineIcon icon={iconData} />);
const tree = component.toJSON();
expect(tree).toMatchObject({
type: 'svg',
props: {
'xmlns': 'http://www.w3.org/2000/svg',
'xmlnsXlink': 'http://www.w3.org/1999/xlink',
'aria-hidden': true,
'role': 'img',
'style': {
verticalAlign: '-0.125em',
},
'dangerouslySetInnerHTML': {
__html: iconData.body,
},
'width': '1em',
'height': '1em',
'viewBox': '0 0 ' + iconData.width + ' ' + iconData.height,
},
children: null,
});
});
});

View File

@ -0,0 +1,35 @@
import React from 'react';
import { Icon, InlineIcon } from '../../dist/iconify';
import { describe, test, expect } from 'vitest';
import { render } from '@testing-library/react';
const iconData = {
body: '<path d="M4 19h16v2H4zm5-4h11v2H9zm-5-4h16v2H4zm0-8h16v2H4zm5 4h11v2H9z" fill="currentColor"/>',
width: 24,
height: 24,
};
describe('Creating component using object', () => {
test('basic icon', () => {
const renderResult = render(
<Icon
icon={iconData}
onLoad={() => {
// Should be called only for icons loaded from API
throw new Error('onLoad called for object!');
}}
/>
);
expect(renderResult.container.innerHTML).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" viewBox="0 0 24 24"><path d="M4 19h16v2H4zm5-4h11v2H9zm-5-4h16v2H4zm0-8h16v2H4zm5 4h11v2H9z" fill="currentColor"></path></svg>'
);
});
test('inline icon', () => {
const renderResult = render(<InlineIcon icon={iconData} />);
expect(renderResult.container.innerHTML).toEqual(
'<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" aria-hidden="true" role="img" style="vertical-align: -0.125em;" width="1em" height="1em" viewBox="0 0 24 24"><path d="M4 19h16v2H4zm5-4h11v2H9zm-5-4h16v2H4zm0-8h16v2H4zm5 4h11v2H9z" fill="currentColor"></path></svg>'
);
});
});

View File

@ -1,57 +0,0 @@
import React from 'react';
import { Icon } from '../../dist/iconify';
import renderer from 'react-test-renderer';
describe('Empty icon', () => {
test('basic test', () => {
const component = renderer.create(<Icon />);
const tree = component.toJSON();
expect(tree).toMatchObject({
type: 'span',
props: {},
children: null,
});
});
test('with child node', () => {
const component = renderer.create(
<Icon>
<i class="fa fa-home" />
</Icon>
);
const tree = component.toJSON();
expect(tree).toMatchObject({
type: 'i',
props: {},
children: null,
});
});
test('with text child node', () => {
const component = renderer.create(<Icon>icon</Icon>);
const tree = component.toJSON();
expect(tree).toMatch('icon');
});
test('with multiple childen', () => {
const component = renderer.create(
<Icon>
<i class="fa fa-home" />
Home
</Icon>
);
const tree = component.toJSON();
expect(tree).toMatchObject([
{
type: 'i',
props: {},
children: null,
},
'Home',
]);
});
});

View File

@ -0,0 +1,46 @@
import React from 'react';
import { Icon } from '../../dist/iconify';
import { describe, test, expect } from 'vitest';
import { render } from '@testing-library/react';
describe('Empty icon', () => {
test('basic test', () => {
// @ts-expect-error
const renderResult = render(<Icon />);
expect(renderResult.container.innerHTML).toEqual('<span></span>');
});
test('with child node', () => {
const renderResult = render(
// @ts-expect-error
<Icon>
<i className="fa fa-home" />
</Icon>
);
expect(renderResult.container.innerHTML).toEqual(
'<i class="fa fa-home"></i>'
);
});
test('with text child node', () => {
// @ts-expect-error
const renderResult = render(<Icon>icon</Icon>);
expect(renderResult.container.innerHTML).toEqual('icon');
});
test('with multiple childen', () => {
const renderResult = render(
// @ts-expect-error
<Icon>
<i className="fa fa-home" />
Home
</Icon>
);
expect(renderResult.container.innerHTML).toEqual(
'<i class="fa fa-home"></i>Home'
);
});
});

View File

@ -1,81 +0,0 @@
import React from 'react';
import { Icon } from '../../dist/iconify';
import renderer from 'react-test-renderer';
const iconData = {
body: '<path d="M4 19h16v2H4zm5-4h11v2H9zm-5-4h16v2H4zm0-8h16v2H4zm5 4h11v2H9z" fill="currentColor"/>',
width: 24,
height: 24,
};
describe('Rendering as span', () => {
test('basic icon', () => {
const component = renderer.create(
<Icon
icon={iconData}
mode="style"
onLoad={() => {
// Should be called only for icons loaded from API
throw new Error('onLoad called for object!');
}}
/>
);
const tree = component.toJSON();
expect(tree).toMatchObject({
type: 'span',
props: {
style: {
'--svg': `url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24'%3E%3Cpath d='M4 19h16v2H4zm5-4h11v2H9zm-5-4h16v2H4zm0-8h16v2H4zm5 4h11v2H9z' fill='currentColor'/%3E%3C/svg%3E")`,
'width': '1em',
'height': '1em',
'display': 'inline-block',
'backgroundColor': 'currentColor',
// 'webkitMaskImage': 'var(--svg)',
// 'webkitMaskRepeat': 'no-repeat',
// 'webkitMaskSize': '100% 100%',
'maskImage': 'var(--svg)',
'maskRepeat': 'no-repeat',
'maskSize': '100% 100%',
},
},
children: null,
});
});
test('custom dimensions', () => {
const component = renderer.create(
<Icon
icon={iconData}
mode="style"
width="32"
height={48}
onLoad={() => {
// Should be called only for icons loaded from API
throw new Error('onLoad called for object!');
}}
/>
);
const tree = component.toJSON();
expect(tree).toMatchObject({
type: 'span',
props: {
style: {
'--svg': `url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24'%3E%3Cpath d='M4 19h16v2H4zm5-4h11v2H9zm-5-4h16v2H4zm0-8h16v2H4zm5 4h11v2H9z' fill='currentColor'/%3E%3C/svg%3E")`,
'width': '32px',
'height': '48px',
'display': 'inline-block',
'backgroundColor': 'currentColor',
// 'webkitMaskImage': 'var(--svg)',
// 'webkitMaskRepeat': 'no-repeat',
// 'webkitMaskSize': '100% 100%',
'maskImage': 'var(--svg)',
'maskRepeat': 'no-repeat',
'maskSize': '100% 100%',
},
},
children: null,
});
});
});

View File

@ -0,0 +1,48 @@
import React from 'react';
import { Icon } from '../../dist/iconify';
import { describe, test, expect } from 'vitest';
import { render } from '@testing-library/react';
const iconData = {
body: '<path d="M4 19h16v2H4zm5-4h11v2H9zm-5-4h16v2H4zm0-8h16v2H4zm5 4h11v2H9z" fill="currentColor"/>',
width: 24,
height: 24,
};
describe('Rendering as span', () => {
test('basic icon', () => {
const renderResult = render(
<Icon
icon={iconData}
mode="style"
onLoad={() => {
// Should be called only for icons loaded from API
throw new Error('onLoad called for object!');
}}
/>
);
expect(renderResult.container.innerHTML).toEqual(
`<span style="--svg: url(&quot;data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24'%3E%3Cpath d='M4 19h16v2H4zm5-4h11v2H9zm-5-4h16v2H4zm0-8h16v2H4zm5 4h11v2H9z' fill='currentColor'/%3E%3C/svg%3E&quot;); width: 1em; height: 1em; display: inline-block; background-color: currentColor; mask-image: var(--svg); mask-repeat: no-repeat; mask-size: 100% 100%;"></span>`
);
});
test('custom dimensions', () => {
const renderResult = render(
<Icon
icon={iconData}
mode="style"
width="32"
height={48}
onLoad={() => {
// Should be called only for icons loaded from API
throw new Error('onLoad called for object!');
}}
/>
);
expect(renderResult.container.innerHTML).toEqual(
`<span style="--svg: url(&quot;data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24'%3E%3Cpath d='M4 19h16v2H4zm5-4h11v2H9zm-5-4h16v2H4zm0-8h16v2H4zm5 4h11v2H9z' fill='currentColor'/%3E%3C/svg%3E&quot;); width: 32px; height: 48px; display: inline-block; background-color: currentColor; mask-image: var(--svg); mask-repeat: no-repeat; mask-size: 100% 100%;"></span>`
);
});
});

View File

@ -1,83 +0,0 @@
import React from 'react';
import { Icon, InlineIcon } from '../../dist/iconify';
import renderer from 'react-test-renderer';
const iconData = {
body: '<path d="M4 19h16v2H4zm5-4h11v2H9zm-5-4h16v2H4zm0-8h16v2H4zm5 4h11v2H9z" fill="currentColor"/>',
width: 24,
height: 24,
};
describe('Passing attributes', () => {
test('title', () => {
const component = renderer.create(
<Icon icon={iconData} title="Icon!" />
);
const tree = component.toJSON();
expect(tree.props.title).toStrictEqual('Icon!');
});
test('aria-hidden', () => {
const component = renderer.create(
<InlineIcon icon={iconData} aria-hidden="false" />
);
const tree = component.toJSON();
expect(tree.props['aria-hidden']).toStrictEqual(void 0);
});
test('ariaHidden', () => {
const component = renderer.create(
<InlineIcon icon={iconData} ariaHidden="false" />
);
const tree = component.toJSON();
expect(tree.props['aria-hidden']).toStrictEqual(void 0);
});
test('style', () => {
const component = renderer.create(
<InlineIcon
icon={iconData}
style={{ verticalAlign: '0', color: 'red' }}
/>
);
const tree = component.toJSON();
expect(tree.props.style).toMatchObject({
verticalAlign: '0',
color: 'red',
});
});
test('color', () => {
const component = renderer.create(<Icon icon={iconData} color="red" />);
const tree = component.toJSON();
expect(tree.props.style).toMatchObject({
color: 'red',
});
});
test('color with style', () => {
const component = renderer.create(
<Icon icon={iconData} color="red" style={{ color: 'green' }} />
);
const tree = component.toJSON();
// `style` overrides `color`
expect(tree.props.style).toMatchObject({
color: 'green',
});
});
test('attributes that cannot change', () => {
const component = renderer.create(
<InlineIcon icon={iconData} viewBox="0 0 0 0" />
);
const tree = component.toJSON();
expect(tree.props.viewBox).toStrictEqual('0 0 24 24');
});
});

View File

@ -0,0 +1,75 @@
import React from 'react';
import { Icon, InlineIcon } from '../../dist/iconify';
import { describe, test, expect } from 'vitest';
import { render } from '@testing-library/react';
const iconData = {
body: '<path d="M4 19h16v2H4zm5-4h11v2H9zm-5-4h16v2H4zm0-8h16v2H4zm5 4h11v2H9z" fill="currentColor"/>',
width: 24,
height: 24,
};
describe('Passing attributes', () => {
test('title', () => {
// @ts-expect-error
const renderResult = render(<Icon icon={iconData} title="Icon!" />);
expect(renderResult.container.innerHTML).toContain('title="Icon!"');
});
test('aria-hidden', () => {
const renderResult = render(
<InlineIcon icon={iconData} aria-hidden="false" />
);
expect(renderResult.container.innerHTML).not.toContain('aria-hidden');
});
test('ariaHidden', () => {
const renderResult = render(
// @ts-expect-error
<InlineIcon icon={iconData} ariaHidden="false" />
);
expect(renderResult.container.innerHTML).not.toContain('aria-hidden');
});
test('style', () => {
const renderResult = render(
<InlineIcon
icon={iconData}
style={{ verticalAlign: '0', color: 'red' }}
/>
);
expect(renderResult.container.innerHTML).toContain(
'style="vertical-align: 0; color: red;"'
);
});
test('color', () => {
const renderResult = render(<Icon icon={iconData} color="red" />);
expect(renderResult.container.innerHTML).toContain(
'style="color: red;"'
);
});
test('color with style', () => {
const renderResult = render(
<Icon icon={iconData} color="red" style={{ color: 'green' }} />
);
// `style` overrides `color`
expect(renderResult.container.innerHTML).toContain(
'style="color: green;"'
);
expect(renderResult.container.innerHTML).not.toContain('red');
});
test('attributes that cannot change', () => {
const renderResult = render(
<InlineIcon icon={iconData} viewBox="0 0 0 0" />
);
expect(renderResult.container.innerHTML).toContain(
'viewBox="0 0 24 24"'
);
expect(renderResult.container.innerHTML).not.toContain('0 0 0 0');
});
});

View File

@ -1,51 +0,0 @@
import React from 'react';
import { InlineIcon } from '../../dist/iconify';
import renderer from 'react-test-renderer';
const iconData = {
body: '<path d="M4 19h16v2H4zm5-4h11v2H9zm-5-4h16v2H4zm0-8h16v2H4zm5 4h11v2H9z" fill="currentColor"/>',
width: 24,
height: 24,
};
describe('Dimensions', () => {
test('height', () => {
const component = renderer.create(
<InlineIcon icon={iconData} height="48" />
);
const tree = component.toJSON();
expect(tree.props.height).toStrictEqual('48');
expect(tree.props.width).toStrictEqual('48');
});
test('width and height', () => {
const component = renderer.create(
<InlineIcon icon={iconData} width={32} height="48" />
);
const tree = component.toJSON();
expect(tree.props.height).toStrictEqual('48');
expect(tree.props.width).toStrictEqual('32');
});
test('auto', () => {
const component = renderer.create(
<InlineIcon icon={iconData} height="auto" />
);
const tree = component.toJSON();
expect(tree.props.height).toStrictEqual('24');
expect(tree.props.width).toStrictEqual('24');
});
test('unset', () => {
const component = renderer.create(
<InlineIcon icon={iconData} height="unset" />
);
const tree = component.toJSON();
expect(tree.props.height).toStrictEqual(void 0);
expect(tree.props.width).toStrictEqual(void 0);
});
});

View File

@ -0,0 +1,48 @@
import React from 'react';
import { InlineIcon } from '../../dist/iconify';
import { describe, test, expect } from 'vitest';
import { render } from '@testing-library/react';
const iconData = {
body: '<path d="M4 19h16v2H4zm5-4h11v2H9zm-5-4h16v2H4zm0-8h16v2H4zm5 4h11v2H9z" fill="currentColor"/>',
width: 24,
height: 24,
};
describe('Dimensions', () => {
test('height', () => {
const renderResult = render(<InlineIcon icon={iconData} height="48" />);
expect(renderResult.container.innerHTML).toContain(
'width="48" height="48"'
);
expect(renderResult.container.innerHTML).not.toContain('1em');
});
test('width and height', () => {
const renderResult = render(
<InlineIcon icon={iconData} width={32} height="48" />
);
expect(renderResult.container.innerHTML).toContain(
'width="32" height="48"'
);
expect(renderResult.container.innerHTML).not.toContain('1em');
});
test('auto', () => {
const renderResult = render(
<InlineIcon icon={iconData} height="auto" />
);
expect(renderResult.container.innerHTML).toContain(
'width="24" height="24"'
);
expect(renderResult.container.innerHTML).not.toContain('1em');
});
test('unset', () => {
const renderResult = render(
<InlineIcon icon={iconData} height="unset" />
);
expect(renderResult.container.innerHTML).not.toContain('width=');
expect(renderResult.container.innerHTML).not.toContain('height=');
});
});

View File

@ -1,6 +1,7 @@
import React from 'react';
import { Icon } from '../../dist/iconify';
import renderer from 'react-test-renderer';
import { describe, test, expect } from 'vitest';
import { render } from '@testing-library/react';
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>',
@ -10,47 +11,18 @@ const iconDataWithID = {
describe('Replacing IDs', () => {
test('default behavior', () => {
const component = renderer.create(<Icon icon={iconDataWithID} />);
const tree = component.toJSON();
const body = tree.props.dangerouslySetInnerHTML.__html;
expect(body).not.toStrictEqual(iconDataWithID.body);
const renderResult = render(<Icon icon={iconDataWithID} />);
expect(renderResult.container.innerHTML).not.toContain(
'ssvg-id-1st-place-medal'
);
});
test('custom generator', () => {
const component = renderer.create(
<Icon icon={iconDataWithID} id="test" />
const renderResult = render(<Icon icon={iconDataWithID} id="test" />);
expect(renderResult.container.innerHTML).not.toContain(
'ssvg-id-1st-place-medal'
);
const tree = component.toJSON();
const body = tree.props.dangerouslySetInnerHTML.__html;
// Generate expected body
let expected = iconDataWithID.body;
const replacements = {
'ssvg-id-1st-place-medala': 'testID0',
'ssvg-id-1st-place-medald': 'testID1',
'ssvg-id-1st-place-medalf': 'testID2',
'ssvg-id-1st-place-medalh': 'testID3',
'ssvg-id-1st-place-medalj': 'testID4',
'ssvg-id-1st-place-medalm': 'testID5',
'ssvg-id-1st-place-medalp': 'testID6',
'ssvg-id-1st-place-medalb': 'testID7',
'ssvg-id-1st-place-medalk': 'testID8',
'ssvg-id-1st-place-medalo': 'testID9',
'ssvg-id-1st-place-medalc': 'testID10',
'ssvg-id-1st-place-medale': 'testID11',
'ssvg-id-1st-place-medalg': 'testID12',
'ssvg-id-1st-place-medali': 'testID13',
'ssvg-id-1st-place-medall': 'testID14',
'ssvg-id-1st-place-medaln': 'testID15',
};
Object.keys(replacements).forEach((search) => {
expected = expected.replace(
new RegExp(search, 'g'),
replacements[search]
);
});
expect(body).toStrictEqual(expected);
expect(renderResult.container.innerHTML).toContain('id="testID0"');
expect(renderResult.container.innerHTML).toContain('id="testID10"');
});
});

View File

@ -1,30 +0,0 @@
import React from 'react';
import { Icon } from '../../dist/iconify';
import renderer from 'react-test-renderer';
const iconData = {
body: '<path d="M4 19h16v2H4zm5-4h11v2H9zm-5-4h16v2H4zm0-8h16v2H4zm5 4h11v2H9z" fill="currentColor"/>',
width: 24,
height: 24,
};
describe('Inline attribute', () => {
test('string', () => {
const component = renderer.create(
<Icon icon={iconData} inline="true" />
);
const tree = component.toJSON();
expect(tree.props.style.verticalAlign).toStrictEqual('-0.125em');
});
test('false string', () => {
// "false" should be ignored
const component = renderer.create(
<Icon icon={iconData} inline="false" />
);
const tree = component.toJSON();
expect(tree.props.style.verticalAlign).toEqual(void 0);
});
});

View File

@ -0,0 +1,43 @@
import React from 'react';
import { Icon } from '../../dist/iconify';
import { describe, test, expect } from 'vitest';
import { render } from '@testing-library/react';
const iconData = {
body: '<path d="M4 19h16v2H4zm5-4h11v2H9zm-5-4h16v2H4zm0-8h16v2H4zm5 4h11v2H9z" fill="currentColor"/>',
width: 24,
height: 24,
};
describe('Inline attribute', () => {
test('boolean true', () => {
const renderResult = render(<Icon icon={iconData} inline={true} />);
expect(renderResult.container.innerHTML).toContain(
'style="vertical-align: -0.125em;"'
);
});
test('string', () => {
// @ts-expect-error
const renderResult = render(<Icon icon={iconData} inline="true" />);
expect(renderResult.container.innerHTML).toContain(
'style="vertical-align: -0.125em;"'
);
});
test('false', () => {
const renderResult = render(<Icon icon={iconData} inline={false} />);
expect(renderResult.container.innerHTML).not.toContain(
'vertical-align'
);
});
test('false string', () => {
// "false" should be ignored
// @ts-expect-error
const renderResult = render(<Icon icon={iconData} inline="false" />);
expect(renderResult.container.innerHTML).not.toContain(
'vertical-align'
);
});
});

View File

@ -1,10 +1,10 @@
import React from 'react';
import { Icon, InlineIcon } from '../../dist/iconify';
import renderer from 'react-test-renderer';
import { describe, test, expect } from 'vitest';
import { render } from '@testing-library/react';
const iconData = {
body:
'<path d="M4 19h16v2H4zm5-4h11v2H9zm-5-4h16v2H4zm0-8h16v2H4zm5 4h11v2H9z" fill="currentColor"/>',
body: '<path d="M4 19h16v2H4zm5-4h11v2H9zm-5-4h16v2H4zm0-8h16v2H4zm5 4h11v2H9z" fill="currentColor"/>',
width: 24,
height: 24,
};
@ -12,7 +12,7 @@ const iconData = {
describe('Testing references', () => {
test('basic icon reference', () => {
let gotRef = false;
const component = renderer.create(
render(
<Icon
icon={iconData}
ref={(element) => {
@ -27,7 +27,7 @@ describe('Testing references', () => {
test('inline icon reference', () => {
let gotRef = false;
const component = renderer.create(
render(
<InlineIcon
icon={iconData}
ref={(element) => {
@ -42,7 +42,8 @@ describe('Testing references', () => {
test('placeholder reference', () => {
let gotRef = false;
const component = renderer.create(
render(
// @ts-expect-error
<Icon
ref={(element) => {
gotRef = true;

View File

@ -1,114 +0,0 @@
import React from 'react';
import { InlineIcon } from '../../dist/iconify';
import renderer from 'react-test-renderer';
const iconData = {
body: '<path d="M4 19h16v2H4zm5-4h11v2H9zm-5-4h16v2H4zm0-8h16v2H4zm5 4h11v2H9z" fill="currentColor"/>',
width: 24,
height: 24,
};
describe('Rotation', () => {
test('number', () => {
const component = renderer.create(
<InlineIcon icon={iconData} rotate={1} />
);
const tree = component.toJSON();
const body = tree.props.dangerouslySetInnerHTML.__html;
expect(body).not.toStrictEqual(iconData.body);
expect(body).toMatch('rotate(90 ');
});
test('string', () => {
const component = renderer.create(
<InlineIcon icon={iconData} rotate="180deg" />
);
const tree = component.toJSON();
const body = tree.props.dangerouslySetInnerHTML.__html;
expect(body).not.toStrictEqual(iconData.body);
expect(body).toMatch('rotate(180 ');
});
});
describe('Flip', () => {
test('boolean', () => {
const component = renderer.create(
<InlineIcon icon={iconData} hFlip={true} />
);
const tree = component.toJSON();
const body = tree.props.dangerouslySetInnerHTML.__html;
expect(body).not.toStrictEqual(iconData.body);
expect(body).toMatch('scale(-1 1)');
});
test('string', () => {
const component = renderer.create(
<InlineIcon icon={iconData} flip="vertical" />
);
const tree = component.toJSON();
const body = tree.props.dangerouslySetInnerHTML.__html;
expect(body).not.toStrictEqual(iconData.body);
expect(body).toMatch('scale(1 -1)');
});
test('string and boolean', () => {
const component = renderer.create(
<InlineIcon icon={iconData} flip="horizontal" vFlip={true} />
);
const tree = component.toJSON();
const body = tree.props.dangerouslySetInnerHTML.__html;
expect(body).not.toStrictEqual(iconData.body);
// horizontal + vertical = 180deg rotation
expect(body).toMatch('rotate(180 ');
});
test('string for boolean attribute', () => {
const component = renderer.create(
<InlineIcon icon={iconData} hFlip="true" />
);
const tree = component.toJSON();
const body = tree.props.dangerouslySetInnerHTML.__html;
expect(body).not.toStrictEqual(iconData.body);
expect(body).toMatch('scale(-1 1)');
});
test('shorthand and boolean', () => {
// 'flip' is processed after 'hFlip' because of order of elements in object, overwriting value
const component = renderer.create(
<InlineIcon icon={iconData} hFlip={false} flip="horizontal" />
);
const tree = component.toJSON();
const body = tree.props.dangerouslySetInnerHTML.__html;
expect(body).not.toStrictEqual(iconData.body);
expect(body).toMatch('scale(-1 1)');
});
test('shorthand and boolean as string', () => {
const component = renderer.create(
<InlineIcon icon={iconData} flip="vertical" hFlip="true" />
);
const tree = component.toJSON();
const body = tree.props.dangerouslySetInnerHTML.__html;
expect(body).not.toStrictEqual(iconData.body);
// horizontal + vertical = 180deg rotation
expect(body).toMatch('rotate(180 ');
});
test('wrong case', () => {
const component = renderer.create(
<InlineIcon icon={iconData} vflip={true} />
);
const tree = component.toJSON();
const body = tree.props.dangerouslySetInnerHTML.__html;
expect(body).not.toMatch('scale(');
});
});

View File

@ -0,0 +1,100 @@
import React from 'react';
import { InlineIcon } from '../../dist/iconify';
import { describe, test, expect } from 'vitest';
import { render } from '@testing-library/react';
const iconData = {
body: '<path d="M4 19h16v2H4zm5-4h11v2H9zm-5-4h16v2H4zm0-8h16v2H4zm5 4h11v2H9z" fill="currentColor"/>',
width: 24,
height: 24,
};
describe('Rotation', () => {
test('number', () => {
const renderResult = render(<InlineIcon icon={iconData} rotate={1} />);
expect(renderResult.container.innerHTML).toContain(
'<g transform="rotate(90 12 12)">'
);
});
test('string', () => {
const renderResult = render(
// @ts-expect-error
<InlineIcon icon={iconData} rotate="180deg" />
);
expect(renderResult.container.innerHTML).toContain(
'<g transform="rotate(180 12 12)">'
);
});
});
describe('Flip', () => {
test('boolean', () => {
const renderResult = render(
<InlineIcon icon={iconData} hFlip={true} />
);
expect(renderResult.container.innerHTML).toContain(
'<g transform="translate(24 0) scale(-1 1)">'
);
});
test('string', () => {
const renderResult = render(
<InlineIcon icon={iconData} flip="vertical" />
);
expect(renderResult.container.innerHTML).toContain(
'<g transform="translate(0 24) scale(1 -1)">'
);
});
test('string and boolean', () => {
const renderResult = render(
<InlineIcon icon={iconData} flip="horizontal" vFlip={true} />
);
// horizontal + vertical = 180deg rotation
expect(renderResult.container.innerHTML).toContain(
'<g transform="rotate(180 12 12)">'
);
expect(renderResult.container.innerHTML).not.toContain('scale');
});
test('string for boolean attribute', () => {
const renderResult = render(
// @ts-expect-error
<InlineIcon icon={iconData} hFlip="true" />
);
expect(renderResult.container.innerHTML).toContain(
'<g transform="translate(24 0) scale(-1 1)">'
);
});
test('shorthand and boolean', () => {
// 'flip' is processed after 'hFlip' because of order of elements in object, overwriting value
const renderResult = render(
<InlineIcon icon={iconData} hFlip={false} flip="horizontal" />
);
expect(renderResult.container.innerHTML).toContain(
'<g transform="translate(24 0) scale(-1 1)">'
);
});
test('shorthand and boolean as string', () => {
const renderResult = render(
// @ts-expect-error
<InlineIcon icon={iconData} flip="vertical" hFlip="true" />
);
// horizontal + vertical = 180deg rotation
expect(renderResult.container.innerHTML).toContain(
'<g transform="rotate(180 12 12)">'
);
expect(renderResult.container.innerHTML).not.toContain('scale');
});
test('wrong case', () => {
const renderResult = render(
// @ts-expect-error
<InlineIcon icon={iconData} vflip={true} />
);
expect(renderResult.container.innerHTML).not.toContain('transform');
});
});

View File

@ -1,59 +0,0 @@
import React from 'react';
import { Icon, InlineIcon } from '../../dist/offline';
import renderer from 'react-test-renderer';
const iconData = {
body: '<path d="M4 19h16v2H4zm5-4h11v2H9zm-5-4h16v2H4zm0-8h16v2H4zm5 4h11v2H9z" fill="currentColor"/>',
width: 24,
height: 24,
};
describe('Creating component', () => {
test('basic icon', () => {
const component = renderer.create(<Icon icon={iconData} />);
const tree = component.toJSON();
expect(tree).toMatchObject({
type: 'svg',
props: {
'xmlns': 'http://www.w3.org/2000/svg',
'xmlnsXlink': 'http://www.w3.org/1999/xlink',
'aria-hidden': true,
'role': 'img',
'style': {},
'dangerouslySetInnerHTML': {
__html: iconData.body,
},
'width': '1em',
'height': '1em',
'viewBox': '0 0 ' + iconData.width + ' ' + iconData.height,
},
children: null,
});
});
test('inline icon', () => {
const component = renderer.create(<InlineIcon icon={iconData} />);
const tree = component.toJSON();
expect(tree).toMatchObject({
type: 'svg',
props: {
'xmlns': 'http://www.w3.org/2000/svg',
'xmlnsXlink': 'http://www.w3.org/1999/xlink',
'aria-hidden': true,
'role': 'img',
'style': {
verticalAlign: '-0.125em',
},
'dangerouslySetInnerHTML': {
__html: iconData.body,
},
'width': '1em',
'height': '1em',
'viewBox': '0 0 ' + iconData.width + ' ' + iconData.height,
},
children: null,
});
});
});

View File

@ -0,0 +1,23 @@
import React from 'react';
import { Icon, InlineIcon } from '../../dist/offline';
import { describe, test, expect } from 'vitest';
import { render } from '@testing-library/react';
const iconData = {
body: '<path d="M4 19h16v2H4zm5-4h11v2H9zm-5-4h16v2H4zm0-8h16v2H4zm5 4h11v2H9z" fill="currentColor"/>',
width: 24,
height: 24,
};
describe('Creating component', () => {
test('basic icon', () => {
const renderResult = render(<Icon icon={iconData} />);
});
test('inline icon', () => {
const renderResult = render(<InlineIcon icon={iconData} />);
expect(renderResult.container.innerHTML).toEqual(
'<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" aria-hidden="true" role="img" style="vertical-align: -0.125em;" width="1em" height="1em" viewBox="0 0 24 24"><path d="M4 19h16v2H4zm5-4h11v2H9zm-5-4h16v2H4zm0-8h16v2H4zm5 4h11v2H9z" fill="currentColor"></path></svg>'
);
});
});

View File

@ -1,57 +0,0 @@
import React from 'react';
import { Icon } from '../../dist/offline';
import renderer from 'react-test-renderer';
describe('Empty icon', () => {
test('basic test', () => {
const component = renderer.create(<Icon />);
const tree = component.toJSON();
expect(tree).toMatchObject({
type: 'span',
props: {},
children: null,
});
});
test('with child node', () => {
const component = renderer.create(
<Icon>
<i class="fa fa-home" />
</Icon>
);
const tree = component.toJSON();
expect(tree).toMatchObject({
type: 'i',
props: {},
children: null,
});
});
test('with text child node', () => {
const component = renderer.create(<Icon>icon</Icon>);
const tree = component.toJSON();
expect(tree).toMatch('icon');
});
test('with multiple childen', () => {
const component = renderer.create(
<Icon>
<i class="fa fa-home" />
Home
</Icon>
);
const tree = component.toJSON();
expect(tree).toMatchObject([
{
type: 'i',
props: {},
children: null,
},
'Home',
]);
});
});

View File

@ -0,0 +1,47 @@
import React from 'react';
import { Icon } from '../../dist/offline';
import { describe, test, expect } from 'vitest';
import { render } from '@testing-library/react';
describe('Empty icon', () => {
test('basic test', () => {
// @ts-expect-error
const renderResult = render(<Icon />);
expect(renderResult.container.innerHTML).toEqual('<span></span>');
});
test('with child node', () => {
const renderResult = render(
// @ts-expect-error
<Icon>
<i className="fa fa-home" />
</Icon>
);
expect(renderResult.container.innerHTML).toEqual(
'<i class="fa fa-home"></i>'
);
});
test('with text child node', () => {
// @ts-expect-error
const renderResult = render(<Icon>icon</Icon>);
expect(renderResult.container.innerHTML).toEqual('icon');
});
test('with multiple childen', () => {
const renderResult = render(
// @ts-expect-error
<Icon>
<i className="fa fa-home" />
Home
</Icon>
);
expect(renderResult.container.innerHTML).toEqual(
'<i class="fa fa-home"></i>Home'
);
});
});

View File

@ -1,85 +0,0 @@
import React from 'react';
import { Icon, InlineIcon } from '../../dist/offline';
import renderer from 'react-test-renderer';
const iconData = {
body: '<path d="M4 19h16v2H4zm5-4h11v2H9zm-5-4h16v2H4zm0-8h16v2H4zm5 4h11v2H9z" fill="currentColor"/>',
width: 24,
height: 24,
};
describe('Passing attributes', () => {
test('title', () => {
const component = renderer.create(
<Icon icon={iconData} title="Icon!" />
);
const tree = component.toJSON();
expect(tree.props.title).toStrictEqual('Icon!');
});
test('aria-hidden', () => {
// dashes, string value
const component = renderer.create(
<InlineIcon icon={iconData} aria-hidden="false" />
);
const tree = component.toJSON();
expect(tree.props['aria-hidden']).toStrictEqual(void 0);
});
test('ariaHidden', () => {
// camelCase, boolean value
const component = renderer.create(
<InlineIcon icon={iconData} ariaHidden={false} />
);
const tree = component.toJSON();
expect(tree.props['aria-hidden']).toStrictEqual(void 0);
});
test('style', () => {
const component = renderer.create(
<InlineIcon
icon={iconData}
style={{ verticalAlign: '0', color: 'red' }}
/>
);
const tree = component.toJSON();
expect(tree.props.style).toMatchObject({
verticalAlign: '0',
color: 'red',
});
});
test('color', () => {
const component = renderer.create(<Icon icon={iconData} color="red" />);
const tree = component.toJSON();
expect(tree.props.style).toMatchObject({
color: 'red',
});
});
test('color with style', () => {
const component = renderer.create(
<Icon icon={iconData} color="red" style={{ color: 'green' }} />
);
const tree = component.toJSON();
// `style` overrides `color`
expect(tree.props.style).toMatchObject({
color: 'green',
});
});
test('attributes that cannot change', () => {
const component = renderer.create(
<InlineIcon icon={iconData} viewBox="0 0 0 0" />
);
const tree = component.toJSON();
expect(tree.props.viewBox).toStrictEqual('0 0 24 24');
});
});

View File

@ -0,0 +1,77 @@
import React from 'react';
import { Icon, InlineIcon } from '../../dist/offline';
import { describe, test, expect } from 'vitest';
import { render } from '@testing-library/react';
const iconData = {
body: '<path d="M4 19h16v2H4zm5-4h11v2H9zm-5-4h16v2H4zm0-8h16v2H4zm5 4h11v2H9z" fill="currentColor"/>',
width: 24,
height: 24,
};
describe('Passing attributes', () => {
test('title', () => {
// @ts-expect-error
const renderResult = render(<Icon icon={iconData} title="Icon!" />);
expect(renderResult.container.innerHTML).toContain('title="Icon!"');
});
test('aria-hidden', () => {
// dashes, string value
const renderResult = render(
<InlineIcon icon={iconData} aria-hidden="false" />
);
expect(renderResult.container.innerHTML).not.toContain('aria-hidden');
});
test('ariaHidden', () => {
// camelCase, boolean value
const renderResult = render(
// @ts-expect-error
<InlineIcon icon={iconData} ariaHidden="false" />
);
expect(renderResult.container.innerHTML).not.toContain('aria-hidden');
});
test('style', () => {
const renderResult = render(
<InlineIcon
icon={iconData}
style={{ verticalAlign: '0', color: 'red' }}
/>
);
expect(renderResult.container.innerHTML).toContain(
'style="vertical-align: 0; color: red;"'
);
});
test('color', () => {
const renderResult = render(<Icon icon={iconData} color="red" />);
expect(renderResult.container.innerHTML).toContain(
'style="color: red;"'
);
});
test('color with style', () => {
const renderResult = render(
<Icon icon={iconData} color="red" style={{ color: 'green' }} />
);
// `style` overrides `color`
expect(renderResult.container.innerHTML).toContain(
'style="color: green;"'
);
expect(renderResult.container.innerHTML).not.toContain('red');
});
test('attributes that cannot change', () => {
const renderResult = render(
<InlineIcon icon={iconData} viewBox="0 0 0 0" />
);
expect(renderResult.container.innerHTML).toContain(
'viewBox="0 0 24 24"'
);
expect(renderResult.container.innerHTML).not.toContain('0 0 0 0');
});
});

View File

@ -1,51 +0,0 @@
import React from 'react';
import { InlineIcon } from '../../dist/offline';
import renderer from 'react-test-renderer';
const iconData = {
body: '<path d="M4 19h16v2H4zm5-4h11v2H9zm-5-4h16v2H4zm0-8h16v2H4zm5 4h11v2H9z" fill="currentColor"/>',
width: 24,
height: 24,
};
describe('Dimensions', () => {
test('height', () => {
const component = renderer.create(
<InlineIcon icon={iconData} height="48" />
);
const tree = component.toJSON();
expect(tree.props.height).toStrictEqual('48');
expect(tree.props.width).toStrictEqual('48');
});
test('width and height', () => {
const component = renderer.create(
<InlineIcon icon={iconData} width={32} height="48" />
);
const tree = component.toJSON();
expect(tree.props.height).toStrictEqual('48');
expect(tree.props.width).toStrictEqual('32');
});
test('auto', () => {
const component = renderer.create(
<InlineIcon icon={iconData} height="auto" />
);
const tree = component.toJSON();
expect(tree.props.height).toStrictEqual('24');
expect(tree.props.width).toStrictEqual('24');
});
test('undefined', () => {
const component = renderer.create(
<InlineIcon icon={iconData} height="undefined" />
);
const tree = component.toJSON();
expect(tree.props.height).toStrictEqual(void 0);
expect(tree.props.width).toStrictEqual(void 0);
});
});

View File

@ -0,0 +1,48 @@
import React from 'react';
import { InlineIcon } from '../../dist/offline';
import { describe, test, expect } from 'vitest';
import { render } from '@testing-library/react';
const iconData = {
body: '<path d="M4 19h16v2H4zm5-4h11v2H9zm-5-4h16v2H4zm0-8h16v2H4zm5 4h11v2H9z" fill="currentColor"/>',
width: 24,
height: 24,
};
describe('Dimensions', () => {
test('height', () => {
const renderResult = render(<InlineIcon icon={iconData} height="48" />);
expect(renderResult.container.innerHTML).toContain(
'width="48" height="48"'
);
expect(renderResult.container.innerHTML).not.toContain('1em');
});
test('width and height', () => {
const renderResult = render(
<InlineIcon icon={iconData} width={32} height="48" />
);
expect(renderResult.container.innerHTML).toContain(
'width="32" height="48"'
);
expect(renderResult.container.innerHTML).not.toContain('1em');
});
test('auto', () => {
const renderResult = render(
<InlineIcon icon={iconData} height="auto" />
);
expect(renderResult.container.innerHTML).toContain(
'width="24" height="24"'
);
expect(renderResult.container.innerHTML).not.toContain('1em');
});
test('unset', () => {
const renderResult = render(
<InlineIcon icon={iconData} height="unset" />
);
expect(renderResult.container.innerHTML).not.toContain('width=');
expect(renderResult.container.innerHTML).not.toContain('height=');
});
});

View File

@ -1,6 +1,7 @@
import React from 'react';
import { Icon } from '../../dist/offline';
import renderer from 'react-test-renderer';
import { describe, test, expect } from 'vitest';
import { render } from '@testing-library/react';
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>',
@ -10,48 +11,18 @@ const iconDataWithID = {
describe('Replacing IDs', () => {
test('default behavior', () => {
const component = renderer.create(<Icon icon={iconDataWithID} />);
const tree = component.toJSON();
const body = tree.props.dangerouslySetInnerHTML.__html;
// Check that default id doesn't exist
expect(body.indexOf('ssvg-id-1st-place-medala')).toEqual(-1);
const renderResult = render(<Icon icon={iconDataWithID} />);
expect(renderResult.container.innerHTML).not.toContain(
'ssvg-id-1st-place-medal'
);
});
test('custom generator', () => {
const component = renderer.create(
<Icon icon={iconDataWithID} id="test" />
const renderResult = render(<Icon icon={iconDataWithID} id="test" />);
expect(renderResult.container.innerHTML).not.toContain(
'ssvg-id-1st-place-medal'
);
const tree = component.toJSON();
const body = tree.props.dangerouslySetInnerHTML.__html;
// Generate expected body
let expected = iconDataWithID.body;
const replacements = {
'ssvg-id-1st-place-medala': 'testID0',
'ssvg-id-1st-place-medald': 'testID1',
'ssvg-id-1st-place-medalf': 'testID2',
'ssvg-id-1st-place-medalh': 'testID3',
'ssvg-id-1st-place-medalj': 'testID4',
'ssvg-id-1st-place-medalm': 'testID5',
'ssvg-id-1st-place-medalp': 'testID6',
'ssvg-id-1st-place-medalb': 'testID7',
'ssvg-id-1st-place-medalk': 'testID8',
'ssvg-id-1st-place-medalo': 'testID9',
'ssvg-id-1st-place-medalc': 'testID10',
'ssvg-id-1st-place-medale': 'testID11',
'ssvg-id-1st-place-medalg': 'testID12',
'ssvg-id-1st-place-medali': 'testID13',
'ssvg-id-1st-place-medall': 'testID14',
'ssvg-id-1st-place-medaln': 'testID15',
};
Object.keys(replacements).forEach((search) => {
expected = expected.replace(
new RegExp(search, 'g'),
replacements[search]
);
});
expect(body).toStrictEqual(expected);
expect(renderResult.container.innerHTML).toContain('id="testID0"');
expect(renderResult.container.innerHTML).toContain('id="testID10"');
});
});

View File

@ -1,48 +0,0 @@
import React from 'react';
import { Icon } from '../../dist/offline';
import renderer from 'react-test-renderer';
const iconData = {
body: '<path d="M4 19h16v2H4zm5-4h11v2H9zm-5-4h16v2H4zm0-8h16v2H4zm5 4h11v2H9z" fill="currentColor"/>',
width: 24,
height: 24,
};
describe('Inline attribute', () => {
test('boolean true', () => {
const component = renderer.create(
<Icon icon={iconData} inline={true} />
);
const tree = component.toJSON();
expect(tree.props.style.verticalAlign).toStrictEqual('-0.125em');
});
test('string', () => {
const component = renderer.create(
<Icon icon={iconData} inline="true" />
);
const tree = component.toJSON();
expect(tree.props.style.verticalAlign).toStrictEqual('-0.125em');
});
test('false', () => {
const component = renderer.create(
<Icon icon={iconData} inline={false} />
);
const tree = component.toJSON();
expect(tree.props.style.verticalAlign).toEqual(void 0);
});
test('false string', () => {
// "false" should be ignored
const component = renderer.create(
<Icon icon={iconData} inline="false" />
);
const tree = component.toJSON();
expect(tree.props.style.verticalAlign).toEqual(void 0);
});
});

View File

@ -0,0 +1,43 @@
import React from 'react';
import { Icon } from '../../dist/offline';
import { describe, test, expect } from 'vitest';
import { render } from '@testing-library/react';
const iconData = {
body: '<path d="M4 19h16v2H4zm5-4h11v2H9zm-5-4h16v2H4zm0-8h16v2H4zm5 4h11v2H9z" fill="currentColor"/>',
width: 24,
height: 24,
};
describe('Inline attribute', () => {
test('boolean true', () => {
const renderResult = render(<Icon icon={iconData} inline={true} />);
expect(renderResult.container.innerHTML).toContain(
'style="vertical-align: -0.125em;"'
);
});
test('string', () => {
// @ts-expect-error
const renderResult = render(<Icon icon={iconData} inline="true" />);
expect(renderResult.container.innerHTML).toContain(
'style="vertical-align: -0.125em;"'
);
});
test('false', () => {
const renderResult = render(<Icon icon={iconData} inline={false} />);
expect(renderResult.container.innerHTML).not.toContain(
'vertical-align'
);
});
test('false string', () => {
// "false" should be ignored
// @ts-expect-error
const renderResult = render(<Icon icon={iconData} inline="false" />);
expect(renderResult.container.innerHTML).not.toContain(
'vertical-align'
);
});
});

View File

@ -1,10 +1,10 @@
import React from 'react';
import { Icon, InlineIcon } from '../../dist/offline';
import renderer from 'react-test-renderer';
import { describe, test, expect } from 'vitest';
import { render } from '@testing-library/react';
const iconData = {
body:
'<path d="M4 19h16v2H4zm5-4h11v2H9zm-5-4h16v2H4zm0-8h16v2H4zm5 4h11v2H9z" fill="currentColor"/>',
body: '<path d="M4 19h16v2H4zm5-4h11v2H9zm-5-4h16v2H4zm0-8h16v2H4zm5 4h11v2H9z" fill="currentColor"/>',
width: 24,
height: 24,
};
@ -12,7 +12,7 @@ const iconData = {
describe('Testing references', () => {
test('basic icon reference', () => {
let gotRef = false;
const component = renderer.create(
render(
<Icon
icon={iconData}
ref={(element) => {
@ -27,7 +27,7 @@ describe('Testing references', () => {
test('inline icon reference', () => {
let gotRef = false;
const component = renderer.create(
render(
<InlineIcon
icon={iconData}
ref={(element) => {
@ -42,7 +42,8 @@ describe('Testing references', () => {
test('placeholder reference', () => {
let gotRef = false;
const component = renderer.create(
render(
// @ts-expect-error
<Icon
ref={(element) => {
gotRef = true;

View File

@ -1,122 +0,0 @@
import React from 'react';
import { Icon, addIcon, addCollection } from '../../dist/offline';
import renderer from 'react-test-renderer';
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 component = renderer.create(<Icon icon="test-icon" />);
const tree = component.toJSON();
expect(tree).toMatchObject({
type: 'svg',
props: {
'xmlns': 'http://www.w3.org/2000/svg',
'xmlnsXlink': 'http://www.w3.org/1999/xlink',
'aria-hidden': true,
'role': 'img',
'style': {},
'dangerouslySetInnerHTML': {
__html: iconData.body,
},
'width': '1em',
'height': '1em',
'viewBox': '0 0 ' + iconData.width + ' ' + iconData.height,
},
children: null,
});
});
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 component = renderer.create(<Icon icon="mdi-light:account" />);
const tree = component.toJSON();
expect(tree).toMatchObject({
type: 'svg',
props: {
'xmlns': 'http://www.w3.org/2000/svg',
'xmlnsXlink': 'http://www.w3.org/1999/xlink',
'aria-hidden': true,
'role': 'img',
'style': {},
'dangerouslySetInnerHTML': {
__html: iconSet.icons.account.body,
},
'width': '1em',
'height': '1em',
'viewBox': '0 0 ' + iconSet.width + ' ' + iconSet.height,
},
children: null,
});
});
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 component = renderer.create(<Icon icon="custom-link" />);
const tree = component.toJSON();
expect(tree).toMatchObject({
type: 'svg',
props: {
'xmlns': 'http://www.w3.org/2000/svg',
'xmlnsXlink': 'http://www.w3.org/1999/xlink',
'aria-hidden': true,
'role': 'img',
'style': {},
'dangerouslySetInnerHTML': {
__html: iconSet.icons.link.body,
},
'width': '1em',
'height': '1em',
'viewBox': '0 0 ' + iconSet.width + ' ' + iconSet.height,
},
children: null,
});
});
test('missing icon from storage', () => {
const component = renderer.create(<Icon icon="missing-icon" />);
const tree = component.toJSON();
expect(tree).toMatchObject({
type: 'span',
props: {},
children: null,
});
});
});

View File

@ -0,0 +1,70 @@
import React from 'react';
import { Icon, addIcon, addCollection } from '../../dist/offline';
import { describe, test, expect } from 'vitest';
import { render } from '@testing-library/react';
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 renderResult = render(<Icon icon="test-icon" />);
expect(renderResult.container.innerHTML).toContain(
'M4 19h16v2H4zm5-4h11v2H9zm-5-4h16v2H4zm0-8h16v2H4zm5 4h11v2H9z'
);
});
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 renderResult = render(<Icon icon="mdi-light:account" />);
expect(renderResult.container.innerHTML).toContain(
'"M11.5 14c4.142 0 7.5 1.567 7.5 3.5V20'
);
});
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 renderResult = render(<Icon icon="custom-link" />);
expect(renderResult.container.innerHTML).toContain(
'M8 13v-1h7v1H8zm7.5-6a5.5 5.5 0 1 1 0 11H13v-1h2.5'
);
});
test('missing icon from storage', () => {
const renderResult = render(<Icon icon="missing-icon" />);
expect(renderResult.container.innerHTML).toEqual('<span></span>');
});
});

View File

@ -1,114 +0,0 @@
import React from 'react';
import { InlineIcon } from '../../dist/offline';
import renderer from 'react-test-renderer';
const iconData = {
body: '<path d="M4 19h16v2H4zm5-4h11v2H9zm-5-4h16v2H4zm0-8h16v2H4zm5 4h11v2H9z" fill="currentColor"/>',
width: 24,
height: 32,
};
describe('Rotation', () => {
test('number', () => {
const component = renderer.create(
<InlineIcon icon={iconData} rotate={1} />
);
const tree = component.toJSON();
const body = tree.props.dangerouslySetInnerHTML.__html;
expect(body).not.toStrictEqual(iconData.body);
expect(body).toMatch('rotate(90 ');
});
test('string', () => {
const component = renderer.create(
<InlineIcon icon={iconData} rotate="180deg" />
);
const tree = component.toJSON();
const body = tree.props.dangerouslySetInnerHTML.__html;
expect(body).not.toStrictEqual(iconData.body);
expect(body).toMatch('rotate(180 ');
});
});
describe('Flip', () => {
test('boolean', () => {
const component = renderer.create(
<InlineIcon icon={iconData} hFlip={true} />
);
const tree = component.toJSON();
const body = tree.props.dangerouslySetInnerHTML.__html;
expect(body).not.toStrictEqual(iconData.body);
expect(body).toMatch('scale(-1 1)');
});
test('string', () => {
const component = renderer.create(
<InlineIcon icon={iconData} flip="vertical" />
);
const tree = component.toJSON();
const body = tree.props.dangerouslySetInnerHTML.__html;
expect(body).not.toStrictEqual(iconData.body);
expect(body).toMatch('scale(1 -1)');
});
test('string and boolean', () => {
const component = renderer.create(
<InlineIcon icon={iconData} flip="horizontal" vFlip={true} />
);
const tree = component.toJSON();
const body = tree.props.dangerouslySetInnerHTML.__html;
expect(body).not.toStrictEqual(iconData.body);
// horizontal + vertical = 180deg rotation
expect(body).toMatch('rotate(180 ');
});
test('string for boolean attribute', () => {
const component = renderer.create(
<InlineIcon icon={iconData} hFlip="true" />
);
const tree = component.toJSON();
const body = tree.props.dangerouslySetInnerHTML.__html;
expect(body).not.toStrictEqual(iconData.body);
expect(body).toMatch('scale(-1 1)');
});
test('shorthand and boolean', () => {
// 'flip' is processed after 'hFlip' because of order of elements in object, overwriting value
const component = renderer.create(
<InlineIcon icon={iconData} hFlip={false} flip="horizontal" />
);
const tree = component.toJSON();
const body = tree.props.dangerouslySetInnerHTML.__html;
expect(body).not.toStrictEqual(iconData.body);
expect(body).toMatch('scale(-1 1)');
});
test('shorthand and boolean as string', () => {
const component = renderer.create(
<InlineIcon icon={iconData} flip="vertical" hFlip="true" />
);
const tree = component.toJSON();
const body = tree.props.dangerouslySetInnerHTML.__html;
expect(body).not.toStrictEqual(iconData.body);
// horizontal + vertical = 180deg rotation
expect(body).toMatch('rotate(180 ');
});
test('wrong case', () => {
const component = renderer.create(
<InlineIcon icon={iconData} vflip={true} />
);
const tree = component.toJSON();
const body = tree.props.dangerouslySetInnerHTML.__html;
expect(body).not.toMatch('scale(');
});
});

View File

@ -0,0 +1,100 @@
import React from 'react';
import { InlineIcon } from '../../dist/offline';
import { describe, test, expect } from 'vitest';
import { render } from '@testing-library/react';
const iconData = {
body: '<path d="M4 19h16v2H4zm5-4h11v2H9zm-5-4h16v2H4zm0-8h16v2H4zm5 4h11v2H9z" fill="currentColor"/>',
width: 24,
height: 24,
};
describe('Rotation', () => {
test('number', () => {
const renderResult = render(<InlineIcon icon={iconData} rotate={1} />);
expect(renderResult.container.innerHTML).toContain(
'<g transform="rotate(90 12 12)">'
);
});
test('string', () => {
const renderResult = render(
// @ts-expect-error
<InlineIcon icon={iconData} rotate="180deg" />
);
expect(renderResult.container.innerHTML).toContain(
'<g transform="rotate(180 12 12)">'
);
});
});
describe('Flip', () => {
test('boolean', () => {
const renderResult = render(
<InlineIcon icon={iconData} hFlip={true} />
);
expect(renderResult.container.innerHTML).toContain(
'<g transform="translate(24 0) scale(-1 1)">'
);
});
test('string', () => {
const renderResult = render(
<InlineIcon icon={iconData} flip="vertical" />
);
expect(renderResult.container.innerHTML).toContain(
'<g transform="translate(0 24) scale(1 -1)">'
);
});
test('string and boolean', () => {
const renderResult = render(
<InlineIcon icon={iconData} flip="horizontal" vFlip={true} />
);
// horizontal + vertical = 180deg rotation
expect(renderResult.container.innerHTML).toContain(
'<g transform="rotate(180 12 12)">'
);
expect(renderResult.container.innerHTML).not.toContain('scale');
});
test('string for boolean attribute', () => {
const renderResult = render(
// @ts-expect-error
<InlineIcon icon={iconData} hFlip="true" />
);
expect(renderResult.container.innerHTML).toContain(
'<g transform="translate(24 0) scale(-1 1)">'
);
});
test('shorthand and boolean', () => {
// 'flip' is processed after 'hFlip' because of order of elements in object, overwriting value
const renderResult = render(
<InlineIcon icon={iconData} hFlip={false} flip="horizontal" />
);
expect(renderResult.container.innerHTML).toContain(
'<g transform="translate(24 0) scale(-1 1)">'
);
});
test('shorthand and boolean as string', () => {
const renderResult = render(
// @ts-expect-error
<InlineIcon icon={iconData} flip="vertical" hFlip="true" />
);
// horizontal + vertical = 180deg rotation
expect(renderResult.container.innerHTML).toContain(
'<g transform="rotate(180 12 12)">'
);
expect(renderResult.container.innerHTML).not.toContain('scale');
});
test('wrong case', () => {
const renderResult = render(
// @ts-expect-error
<InlineIcon icon={iconData} vflip={true} />
);
expect(renderResult.container.innerHTML).not.toContain('transform');
});
});

View File

@ -1,15 +1,11 @@
{
"compilerOptions": {
"rootDir": "./src",
"outDir": "./lib",
"target": "ES2019",
"module": "ESNext",
"declaration": true,
"sourceMap": false,
"strict": false,
"moduleResolution": "node",
"esModuleInterop": true,
"forceConsistentCasingInFileNames": true,
"skipLibCheck": true
}
"files": [],
"references": [
{
"path": "./tsconfig.src.json"
},
{
"path": "./tsconfig.tests.json"
}
]
}

View File

@ -0,0 +1,17 @@
{
"include": ["src/**/*"],
"exclude": ["tests/**/*"],
"compilerOptions": {
"rootDir": "./src",
"outDir": "./lib",
"target": "ES2019",
"module": "ESNext",
"declaration": true,
"sourceMap": false,
"strict": false,
"moduleResolution": "node",
"esModuleInterop": true,
"forceConsistentCasingInFileNames": true,
"skipLibCheck": true
}
}

View File

@ -0,0 +1,18 @@
{
"include": ["tests/**/*", "tests/**/*.tsx"],
"exclude": ["src/*"],
"compilerOptions": {
"rootDir": "./tests",
"target": "ES2019",
"module": "ESNext",
"declaration": false,
"sourceMap": false,
"strict": false,
"moduleResolution": "node",
"esModuleInterop": true,
"forceConsistentCasingInFileNames": true,
"skipLibCheck": true,
"jsx": "react",
"noEmit": true
}
}

View File

@ -0,0 +1,8 @@
import { defineConfig } from 'vitest/config';
export default defineConfig({
test: {
globals: true,
environment: 'jsdom',
},
});

File diff suppressed because it is too large Load Diff