Skip to content

Commit

Permalink
fix: generate random id for svg elements. Fixes #71 (#78)
Browse files Browse the repository at this point in the history
  • Loading branch information
amio committed Jun 22, 2023
1 parent 25ee3bd commit 23108a6
Show file tree
Hide file tree
Showing 3 changed files with 68 additions and 44 deletions.
33 changes: 25 additions & 8 deletions src/index.ts
Expand Up @@ -48,6 +48,9 @@ export function badgen ({
const width = sbRectWidth + stRectWidth
const xlink = icon ? ' xmlns:xlink="http://www.w3.org/1999/xlink"' : ''

const gradientId = generateRandomID(5)
const maskId = generateRandomID(5)

label = label ? sanitize(label) : ''
status = sanitize(status)
color = sanitize(color)
Expand All @@ -74,15 +77,15 @@ export function badgen ({

return `<svg width="${scale * width / 10}" height="${scale * 20}" viewBox="0 0 ${width} 200" xmlns="http://www.w3.org/2000/svg"${xlink} role="img" aria-label="${accessibleText}">
<title>${accessibleText}</title>
<linearGradient id="a" x2="0" y2="100%">
<linearGradient id="${gradientId}" x2="0" y2="100%">
<stop offset="0" stop-opacity=".1" stop-color="#EEE"/>
<stop offset="1" stop-opacity=".1"/>
</linearGradient>
<mask id="m"><rect width="${width}" height="200" rx="30" fill="#FFF"/></mask>
<g mask="url(#m)">
<mask id="${maskId}"><rect width="${width}" height="200" rx="30" fill="#FFF"/></mask>
<g mask="url(#${maskId})">
<rect width="${sbRectWidth}" height="200" fill="#${labelColor}"/>
<rect width="${stRectWidth}" height="200" fill="#${color}" x="${sbRectWidth}"/>
<rect width="${width}" height="200" fill="url(#a)"/>
<rect width="${width}" height="200" fill="url(#${gradientId})"/>
</g>
<g aria-hidden="true" fill="#fff" text-anchor="start" font-family="Verdana,DejaVu Sans,sans-serif" font-size="110">
<text x="${sbTextStart + 10}" y="148" textLength="${sbTextWidth}" fill="#000" opacity="0.25">${label}</text>
Expand All @@ -101,6 +104,9 @@ function bare ({ status, color = 'blue', style, scale = 1 }: BadgenOptions) {
const stTextWidth = calcWidth(status)
const stRectWidth = stTextWidth + 115

const gradientId = generateRandomID(5)
const maskId = generateRandomID(5)

status = sanitize(status)
color = sanitize(color)

Expand All @@ -119,14 +125,14 @@ function bare ({ status, color = 'blue', style, scale = 1 }: BadgenOptions) {

return `<svg width="${scale * stRectWidth / 10}" height="${scale * 20}" viewBox="0 0 ${stRectWidth} 200" xmlns="http://www.w3.org/2000/svg" role="img" aria-label="${status}">
<title>${status}</title>
<linearGradient id="a" x2="0" y2="100%">
<linearGradient id="${gradientId}" x2="0" y2="100%">
<stop offset="0" stop-opacity=".1" stop-color="#EEE"/>
<stop offset="1" stop-opacity=".1"/>
</linearGradient>
<mask id="m"><rect width="${stRectWidth}" height="200" rx="30" fill="#FFF"/></mask>
<g mask="url(#m)">
<mask id="${maskId}"><rect width="${stRectWidth}" height="200" rx="30" fill="#FFF"/></mask>
<g mask="url(#${maskId})">
<rect width="${stRectWidth}" height="200" fill="#${color}" x="0"/>
<rect width="${stRectWidth}" height="200" fill="url(#a)"/>
<rect width="${stRectWidth}" height="200" fill="url(#${gradientId})"/>
</g>
<g aria-hidden="true" fill="#fff" text-anchor="start" font-family="Verdana,DejaVu Sans,sans-serif" font-size="110">
<text x="65" y="148" textLength="${stTextWidth}" fill="#000" opacity="0.25">${status}</text>
Expand All @@ -149,6 +155,17 @@ interface AccessibleTextProps {
label?: string;
}

function generateRandomID(length: number): string {
const characters = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz';
let result = '';

for (let i = 0; i < length; i++) {
result += characters.charAt(Math.floor(Math.random() * characters.length));
}

return result;
}

function createAccessibleText({label, status}: AccessibleTextProps): string {
const labelPrefix = label ? `${label}: ` : '';
return labelPrefix + status;
Expand Down
72 changes: 36 additions & 36 deletions tap-snapshots/test/badgen.spec.js.test.cjs
Expand Up @@ -8,15 +8,15 @@
exports[`test/badgen.spec.js TAP ensure badgen() correctly escapes string inputs > snapshot 1`] = `
<svg width="191.2" height="20" viewBox="0 0 1912 200" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" role="img" aria-label="&lt;escape me&gt;: &lt;escape me&gt;">
<title>&lt;escape me&gt;: &lt;escape me&gt;</title>
<linearGradient id="a" x2="0" y2="100%">
<linearGradient id="aaaaa" x2="0" y2="100%">
<stop offset="0" stop-opacity=".1" stop-color="#EEE"/>
<stop offset="1" stop-opacity=".1"/>
</linearGradient>
<mask id="m"><rect width="1912" height="200" rx="30" fill="#FFF"/></mask>
<g mask="url(#m)">
<mask id="aaaaa"><rect width="1912" height="200" rx="30" fill="#FFF"/></mask>
<g mask="url(#aaaaa)">
<rect width="1036" height="200" fill="#&lt;escape me&gt;"/>
<rect width="876" height="200" fill="#&lt;escape me&gt;" x="1036"/>
<rect width="1912" height="200" fill="url(#a)"/>
<rect width="1912" height="200" fill="url(#aaaaa)"/>
</g>
<g aria-hidden="true" fill="#fff" text-anchor="start" font-family="Verdana,DejaVu Sans,sans-serif" font-size="110">
<text x="220" y="148" textLength="776" fill="#000" opacity="0.25">&lt;escape me&gt;</text>
Expand All @@ -31,14 +31,14 @@ exports[`test/badgen.spec.js TAP ensure badgen() correctly escapes string inputs
exports[`test/badgen.spec.js TAP ensure bare() correctly escapes string inputs > snapshot 1`] = `
<svg width="89.1" height="20" viewBox="0 0 891 200" xmlns="http://www.w3.org/2000/svg" role="img" aria-label="&lt;escape me&gt;">
<title>&lt;escape me&gt;</title>
<linearGradient id="a" x2="0" y2="100%">
<linearGradient id="aaaaa" x2="0" y2="100%">
<stop offset="0" stop-opacity=".1" stop-color="#EEE"/>
<stop offset="1" stop-opacity=".1"/>
</linearGradient>
<mask id="m"><rect width="891" height="200" rx="30" fill="#FFF"/></mask>
<g mask="url(#m)">
<mask id="aaaaa"><rect width="891" height="200" rx="30" fill="#FFF"/></mask>
<g mask="url(#aaaaa)">
<rect width="891" height="200" fill="#&lt;escape me&gt;" x="0"/>
<rect width="891" height="200" fill="url(#a)"/>
<rect width="891" height="200" fill="url(#aaaaa)"/>
</g>
<g aria-hidden="true" fill="#fff" text-anchor="start" font-family="Verdana,DejaVu Sans,sans-serif" font-size="110">
<text x="65" y="148" textLength="776" fill="#000" opacity="0.25">&lt;escape me&gt;</text>
Expand All @@ -50,15 +50,15 @@ exports[`test/badgen.spec.js TAP ensure bare() correctly escapes string inputs >
exports[`test/badgen.spec.js TAP generate badge with { label, status } > snapshot 1`] = `
<svg width="80.4" height="20" viewBox="0 0 804 200" xmlns="http://www.w3.org/2000/svg" role="img" aria-label="npm: v1.0.0">
<title>npm: v1.0.0</title>
<linearGradient id="a" x2="0" y2="100%">
<linearGradient id="aaaaa" x2="0" y2="100%">
<stop offset="0" stop-opacity=".1" stop-color="#EEE"/>
<stop offset="1" stop-opacity=".1"/>
</linearGradient>
<mask id="m"><rect width="804" height="200" rx="30" fill="#FFF"/></mask>
<g mask="url(#m)">
<mask id="aaaaa"><rect width="804" height="200" rx="30" fill="#FFF"/></mask>
<g mask="url(#aaaaa)">
<rect width="349" height="200" fill="#555"/>
<rect width="455" height="200" fill="#08C" x="349"/>
<rect width="804" height="200" fill="url(#a)"/>
<rect width="804" height="200" fill="url(#aaaaa)"/>
</g>
<g aria-hidden="true" fill="#fff" text-anchor="start" font-family="Verdana,DejaVu Sans,sans-serif" font-size="110">
<text x="60" y="148" textLength="249" fill="#000" opacity="0.25">npm</text>
Expand All @@ -73,15 +73,15 @@ exports[`test/badgen.spec.js TAP generate badge with { label, status } > snapsho
exports[`test/badgen.spec.js TAP generate badge with { label, status, color } > snapshot 1`] = `
<svg width="80.4" height="20" viewBox="0 0 804 200" xmlns="http://www.w3.org/2000/svg" role="img" aria-label="npm: v1.0.0">
<title>npm: v1.0.0</title>
<linearGradient id="a" x2="0" y2="100%">
<linearGradient id="aaaaa" x2="0" y2="100%">
<stop offset="0" stop-opacity=".1" stop-color="#EEE"/>
<stop offset="1" stop-opacity=".1"/>
</linearGradient>
<mask id="m"><rect width="804" height="200" rx="30" fill="#FFF"/></mask>
<g mask="url(#m)">
<mask id="aaaaa"><rect width="804" height="200" rx="30" fill="#FFF"/></mask>
<g mask="url(#aaaaa)">
<rect width="349" height="200" fill="#555"/>
<rect width="455" height="200" fill="#ADF" x="349"/>
<rect width="804" height="200" fill="url(#a)"/>
<rect width="804" height="200" fill="url(#aaaaa)"/>
</g>
<g aria-hidden="true" fill="#fff" text-anchor="start" font-family="Verdana,DejaVu Sans,sans-serif" font-size="110">
<text x="60" y="148" textLength="249" fill="#000" opacity="0.25">npm</text>
Expand Down Expand Up @@ -113,15 +113,15 @@ exports[`test/badgen.spec.js TAP generate badge with { label, status, color, sty
exports[`test/badgen.spec.js TAP generate badge with { label, status, icon } > snapshot 1`] = `
<svg width="95.5" height="20" viewBox="0 0 955 200" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" role="img" aria-label="docker: icon">
<title>docker: icon</title>
<linearGradient id="a" x2="0" y2="100%">
<linearGradient id="aaaaa" x2="0" y2="100%">
<stop offset="0" stop-opacity=".1" stop-color="#EEE"/>
<stop offset="1" stop-opacity=".1"/>
</linearGradient>
<mask id="m"><rect width="955" height="200" rx="30" fill="#FFF"/></mask>
<g mask="url(#m)">
<mask id="aaaaa"><rect width="955" height="200" rx="30" fill="#FFF"/></mask>
<g mask="url(#aaaaa)">
<rect width="631" height="200" fill="#555"/>
<rect width="324" height="200" fill="#08C" x="631"/>
<rect width="955" height="200" fill="url(#a)"/>
<rect width="955" height="200" fill="url(#aaaaa)"/>
</g>
<g aria-hidden="true" fill="#fff" text-anchor="start" font-family="Verdana,DejaVu Sans,sans-serif" font-size="110">
<text x="220" y="148" textLength="371" fill="#000" opacity="0.25">docker</text>
Expand Down Expand Up @@ -170,15 +170,15 @@ exports[`test/badgen.spec.js TAP generate badge with { label, status, style } >
exports[`test/badgen.spec.js TAP generate badge with { status, icon } > snapshot 1`] = `
<svg width="53.6" height="20" viewBox="0 0 536 200" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" role="img" aria-label="icon">
<title>icon</title>
<linearGradient id="a" x2="0" y2="100%">
<linearGradient id="aaaaa" x2="0" y2="100%">
<stop offset="0" stop-opacity=".1" stop-color="#EEE"/>
<stop offset="1" stop-opacity=".1"/>
</linearGradient>
<mask id="m"><rect width="536" height="200" rx="30" fill="#FFF"/></mask>
<g mask="url(#m)">
<mask id="aaaaa"><rect width="536" height="200" rx="30" fill="#FFF"/></mask>
<g mask="url(#aaaaa)">
<rect width="212" height="200" fill="#555"/>
<rect width="324" height="200" fill="#08C" x="212"/>
<rect width="536" height="200" fill="url(#a)"/>
<rect width="536" height="200" fill="url(#aaaaa)"/>
</g>
<g aria-hidden="true" fill="#fff" text-anchor="start" font-family="Verdana,DejaVu Sans,sans-serif" font-size="110">
<text x="172" y="148" textLength="0" fill="#000" opacity="0.25"></text>
Expand All @@ -193,15 +193,15 @@ exports[`test/badgen.spec.js TAP generate badge with { status, icon } > snapshot
exports[`test/badgen.spec.js TAP generate badge with { status, icon, iconWidth } > snapshot 1`] = `
<svg width="59.6" height="20" viewBox="0 0 596 200" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" role="img" aria-label="icon">
<title>icon</title>
<linearGradient id="a" x2="0" y2="100%">
<linearGradient id="aaaaa" x2="0" y2="100%">
<stop offset="0" stop-opacity=".1" stop-color="#EEE"/>
<stop offset="1" stop-opacity=".1"/>
</linearGradient>
<mask id="m"><rect width="596" height="200" rx="30" fill="#FFF"/></mask>
<g mask="url(#m)">
<mask id="aaaaa"><rect width="596" height="200" rx="30" fill="#FFF"/></mask>
<g mask="url(#aaaaa)">
<rect width="272" height="200" fill="#555"/>
<rect width="324" height="200" fill="#08C" x="272"/>
<rect width="596" height="200" fill="url(#a)"/>
<rect width="596" height="200" fill="url(#aaaaa)"/>
</g>
<g aria-hidden="true" fill="#fff" text-anchor="start" font-family="Verdana,DejaVu Sans,sans-serif" font-size="110">
<text x="232" y="148" textLength="0" fill="#000" opacity="0.25"></text>
Expand All @@ -216,14 +216,14 @@ exports[`test/badgen.spec.js TAP generate badge with { status, icon, iconWidth }
exports[`test/badgen.spec.js TAP generate bare badge with { status } > snapshot 1`] = `
<svg width="47" height="20" viewBox="0 0 470 200" xmlns="http://www.w3.org/2000/svg" role="img" aria-label="v1.0.0">
<title>v1.0.0</title>
<linearGradient id="a" x2="0" y2="100%">
<linearGradient id="aaaaa" x2="0" y2="100%">
<stop offset="0" stop-opacity=".1" stop-color="#EEE"/>
<stop offset="1" stop-opacity=".1"/>
</linearGradient>
<mask id="m"><rect width="470" height="200" rx="30" fill="#FFF"/></mask>
<g mask="url(#m)">
<mask id="aaaaa"><rect width="470" height="200" rx="30" fill="#FFF"/></mask>
<g mask="url(#aaaaa)">
<rect width="470" height="200" fill="#08C" x="0"/>
<rect width="470" height="200" fill="url(#a)"/>
<rect width="470" height="200" fill="url(#aaaaa)"/>
</g>
<g aria-hidden="true" fill="#fff" text-anchor="start" font-family="Verdana,DejaVu Sans,sans-serif" font-size="110">
<text x="65" y="148" textLength="355" fill="#000" opacity="0.25">v1.0.0</text>
Expand All @@ -235,14 +235,14 @@ exports[`test/badgen.spec.js TAP generate bare badge with { status } > snapshot
exports[`test/badgen.spec.js TAP generate bare badge with { status, color } > snapshot 1`] = `
<svg width="47" height="20" viewBox="0 0 470 200" xmlns="http://www.w3.org/2000/svg" role="img" aria-label="v1.0.0">
<title>v1.0.0</title>
<linearGradient id="a" x2="0" y2="100%">
<linearGradient id="aaaaa" x2="0" y2="100%">
<stop offset="0" stop-opacity=".1" stop-color="#EEE"/>
<stop offset="1" stop-opacity=".1"/>
</linearGradient>
<mask id="m"><rect width="470" height="200" rx="30" fill="#FFF"/></mask>
<g mask="url(#m)">
<mask id="aaaaa"><rect width="470" height="200" rx="30" fill="#FFF"/></mask>
<g mask="url(#aaaaa)">
<rect width="470" height="200" fill="#ADF" x="0"/>
<rect width="470" height="200" fill="url(#a)"/>
<rect width="470" height="200" fill="url(#aaaaa)"/>
</g>
<g aria-hidden="true" fill="#fff" text-anchor="start" font-family="Verdana,DejaVu Sans,sans-serif" font-size="110">
<text x="65" y="148" textLength="355" fill="#000" opacity="0.25">v1.0.0</text>
Expand Down
7 changes: 7 additions & 0 deletions test/badgen.spec.js
Expand Up @@ -2,6 +2,13 @@ const tap = require('tap')
const { badgen } = require('../dist')
const icons = require('./assets/icon-data-uri.js')

const originalMath = global.Math
const mockMath = Object.create(global.Math)
mockMath.random = () => 0.5

tap.beforeEach(async t => { global.Math = mockMath })
tap.afterEach(async t => { global.Math = originalMath })

tap.test('generate badge with { label, status }', t => {
const svg = badgen({ label: 'npm', status: 'v1.0.0' })
t.ok(typeof svg === 'string', 'successfully generated')
Expand Down

0 comments on commit 23108a6

Please sign in to comment.