I wrote simple function to measure texts before I decide what font size should I use.
const svg = document.createElementNS('http://www.w3.org/2000/svg', 'svg');
const text = document.createElementNS('http://www.w3.org/2000/svg', 'text');
svg.appendChild(text);
interface HeightAndWidth {
width: number;
height: number;
}
interface MeasureProps {
label: string;
fontFamily: string;
size: number;
fontWeight?: number;
lineHeight?: number;
letterSpacing?: string;
}
export const measureText = ({
label,
fontFamily,
size,
fontWeight = 400,
lineHeight = 1,
letterSpacing = '0',
}: MeasureProps): HeightAndWidth => {
text.setAttribute('font-size', `${size}px`);
text.setAttribute('line-height', `${lineHeight}px`);
text.setAttribute('font-family', fontFamily);
text.setAttribute('font-weight', `${fontWeight}`);
text.setAttribute('letter-spacing', letterSpacing);
text.textContent = label;
document.body.appendChild(svg);
const { width, height } = text.getBBox();
return {
width,
height,
};
};
It's pretty straightforward and it works nice but I'm having problem writing unit tests for this. The problem is that while working in browser it doesn't work during tests and throws an error
text.getBBox is not a function.
I tried mocking window.SVGTextElement but it keeps throwing error that it's prototype is missing 241 more properties. Even tried it's type but the error stays, and error with missing getBBox repeats.
Is there a way of testing code like this?
import { measureText } from '../measure';
interface MockSvgTextElement {
prototype: {
getBBox: () => { height: number; width: number };
};
}
describe('animateNumber', () => {
beforeEach(() => {
window.SVGTextElement = {
prototype: {
getBBox: () => ({
height: 20,
width: 100,
}),
},
} as MockSvgTextElement;
const p = document.createElement('p');
document.body.appendChild(p);
console.log('window.SVGTextElement', window.SVGTextElement);
});
it('should measure text', () => {
const textSize = measureText(
{
label: '12345',
fontFamily: 'Roboto',
size: 20,
}
);
expect(textSize).toEqual({});
});
});
getBBox can be used for the SVGGraphicsElement type only.
Create an <svg> element under body and then append a <text> under svg.
Then you can call getBBox().width for the text element