Add tests without Intl.Segmenter

This commit is contained in:
Sidharth Vinod
2023-07-07 10:02:04 +05:30
parent b14bcda12a
commit d58c41dbc0
3 changed files with 103 additions and 58 deletions

View File

@@ -14,11 +14,7 @@ function applyStyle(dom, styleFn) {
function addHtmlSpan(element, node, width, classes, addBackground = false) { function addHtmlSpan(element, node, width, classes, addBackground = false) {
const fo = element.append('foreignObject'); const fo = element.append('foreignObject');
// const newEl = document.createElementNS('http://www.w3.org/2000/svg', 'foreignObject');
// const newEl = document.createElementNS('http://www.w3.org/2000/svg', 'foreignObject');
const div = fo.append('xhtml:div'); const div = fo.append('xhtml:div');
// const div = body.append('div');
// const div = fo.append('div');
const label = node.label; const label = node.label;
const labelClass = node.isNode ? 'nodeLabel' : 'edgeLabel'; const labelClass = node.isNode ? 'nodeLabel' : 'edgeLabel';
@@ -130,7 +126,6 @@ function updateTextContentAndStyles(tspan: any, wrappedLine: MarkdownWord[]) {
.attr('font-style', word.type === 'emphasis' ? 'italic' : 'normal') .attr('font-style', word.type === 'emphasis' ? 'italic' : 'normal')
.attr('class', 'text-inner-tspan') .attr('class', 'text-inner-tspan')
.attr('font-weight', word.type === 'strong' ? 'bold' : 'normal'); .attr('font-weight', word.type === 'strong' ? 'bold' : 'normal');
// const special = ['"', "'", '.', ',', ':', ';', '!', '?', '(', ')', '[', ']', '{', '}'];
if (index === 0) { if (index === 0) {
innerTspan.text(word.content); innerTspan.text(word.content);
} else { } else {

View File

@@ -1,46 +1,85 @@
import { splitTextToChars, splitLineToFitWidth, splitLineToWords } from './splitText.js'; import { splitTextToChars, splitLineToFitWidth, splitLineToWords } from './splitText.js';
import { describe, it, expect } from 'vitest'; import { describe, it, expect, vi } from 'vitest';
import type { CheckFitFunction, MarkdownLine, MarkdownWordType } from './types.js'; import type { CheckFitFunction, MarkdownLine, MarkdownWordType } from './types.js';
describe('splitText', () => { describe('when Intl.Segmenter is available', () => {
it.each([ describe('splitText', () => {
{ str: '', split: [] }, it.each([
{ str: '🏳️‍⚧️🏳️‍🌈👩🏾‍❤️‍👨🏻', split: ['🏳️‍⚧️', '🏳️‍🌈', '👩🏾‍❤️‍👨🏻'] }, { str: '', split: [] },
{ str: 'ok', split: ['o', 'k'] }, { str: '🏳️‍⚧️🏳️‍🌈👩🏾‍❤️‍👨🏻', split: ['🏳️‍⚧️', '🏳️‍🌈', '👩🏾‍❤️‍👨🏻'] },
{ str: 'abc', split: ['a', 'b', 'c'] }, { str: 'ok', split: ['o', 'k'] },
])('should split $str into graphemes', ({ str, split }: { str: string; split: string[] }) => { { str: 'abc', split: ['a', 'b', 'c'] },
expect(splitTextToChars(str)).toEqual(split); ])('should split $str into graphemes', ({ str, split }: { str: string; split: string[] }) => {
expect(splitTextToChars(str)).toEqual(split);
});
});
describe('split lines', () => {
it('should create valid checkFit function', () => {
const checkFit5 = createCheckFn(5);
expect(checkFit5([{ content: 'hello', type: 'normal' }])).toBe(true);
expect(
checkFit5([
{ content: 'hello', type: 'normal' },
{ content: 'world', type: 'normal' },
])
).toBe(false);
const checkFit1 = createCheckFn(1);
expect(checkFit1([{ content: 'A', type: 'normal' }])).toBe(true);
expect(checkFit1([{ content: '🏳️‍⚧️', type: 'normal' }])).toBe(true);
expect(checkFit1([{ content: '🏳️‍⚧️🏳️‍⚧️', type: 'normal' }])).toBe(false);
});
it.each([
// empty string
{ str: 'hello world', width: 7, split: ['hello', 'world'] },
// width > full line
{ str: 'hello world', width: 20, split: ['hello world'] },
// width < individual word
{ str: 'hello world', width: 3, split: ['hel', 'lo', 'wor', 'ld'] },
{ str: 'hello 12 world', width: 4, split: ['hell', 'o 12', 'worl', 'd'] },
{ str: 'hello 1 2 world', width: 4, split: ['hell', 'o 1', '2', 'worl', 'd'] },
{ str: 'hello 1 2 world', width: 6, split: ['hello', ' 1 2', 'world'] },
// width = 0, impossible, so split into individual characters
{ str: '🏳️‍⚧️🏳️‍🌈👩🏾‍❤️‍👨🏻', width: 0, split: ['🏳️‍⚧️', '🏳️‍🌈', '👩🏾‍❤️‍👨🏻'] },
{ str: '🏳️‍⚧️🏳️‍🌈👩🏾‍❤️‍👨🏻', width: 1, split: ['🏳️‍⚧️', '🏳️‍🌈', '👩🏾‍❤️‍👨🏻'] },
{ str: '🏳️‍⚧️🏳️‍🌈👩🏾‍❤️‍👨🏻', width: 2, split: ['🏳️‍⚧️🏳️‍🌈', '👩🏾‍❤️‍👨🏻'] },
{ str: '🏳️‍⚧️🏳️‍🌈👩🏾‍❤️‍👨🏻', width: 3, split: ['🏳️‍⚧️🏳️‍🌈👩🏾‍❤️‍👨🏻'] },
{ str: '中文中', width: 1, split: ['中', '文', '中'] },
{ str: '中文中', width: 2, split: ['中文', '中'] },
{ str: '中文中', width: 3, split: ['中文中'] },
{ str: 'Flag 🏳️‍⚧️ this 🏳️‍🌈', width: 6, split: ['Flag 🏳️‍⚧️', 'this 🏳️‍🌈'] },
])(
'should split $str into lines of $width characters',
({ str, split, width }: { str: string; width: number; split: string[] }) => {
const checkFn = createCheckFn(width);
const line: MarkdownLine = getLineFromString(str);
expect(splitLineToFitWidth(line, checkFn)).toEqual(
split.map((str) => getLineFromString(str))
);
}
);
}); });
}); });
describe('split lines', () => { describe('when Intl.segmenter is not available', () => {
/** beforeAll(() => {
* Creates a checkFunction for a given width vi.stubGlobal('Intl', { Segmenter: undefined });
* @param width - width of characters to fit in a line });
* @returns checkFunction afterAll(() => {
*/ vi.unstubAllGlobals();
const createCheckFn = (width: number): CheckFitFunction => { });
return (text: MarkdownLine) => {
// Join all words into a single string
const joinedContent = text.map((w) => w.content).join('');
const characters = splitTextToChars(joinedContent);
return characters.length <= width;
};
};
it('should create valid checkFit function', () => { it.each([
const checkFit5 = createCheckFn(5); { str: '', split: [] },
expect(checkFit5([{ content: 'hello', type: 'normal' }])).toBe(true); {
expect( str: '🏳️‍⚧️🏳️‍🌈👩🏾‍❤️‍👨🏻',
checkFit5([ split: [...'🏳️‍⚧️🏳️‍🌈👩🏾‍❤️‍👨🏻'],
{ content: 'hello', type: 'normal' }, },
{ content: 'world', type: 'normal' }, { str: 'ok', split: ['o', 'k'] },
]) { str: 'abc', split: ['a', 'b', 'c'] },
).toBe(false); ])('should split $str into characters', ({ str, split }: { str: string; split: string[] }) => {
const checkFit1 = createCheckFn(1); expect(splitTextToChars(str)).toEqual(split);
expect(checkFit1([{ content: 'A', type: 'normal' }])).toBe(true);
expect(checkFit1([{ content: '🏳️‍⚧️', type: 'normal' }])).toBe(true);
expect(checkFit1([{ content: '🏳️‍⚧️🏳️‍⚧️', type: 'normal' }])).toBe(false);
}); });
it.each([ it.each([
@@ -52,37 +91,34 @@ describe('split lines', () => {
{ str: 'hello world', width: 3, split: ['hel', 'lo', 'wor', 'ld'] }, { str: 'hello world', width: 3, split: ['hel', 'lo', 'wor', 'ld'] },
{ str: 'hello 12 world', width: 4, split: ['hell', 'o 12', 'worl', 'd'] }, { str: 'hello 12 world', width: 4, split: ['hell', 'o 12', 'worl', 'd'] },
{ str: 'hello 1 2 world', width: 4, split: ['hell', 'o 1', '2', 'worl', 'd'] }, { str: 'hello 1 2 world', width: 4, split: ['hell', 'o 1', '2', 'worl', 'd'] },
{ str: 'hello 1 2 world', width: 6, split: ['hello', ' 1 2', 'world'] }, { str: 'hello 1 2 world', width: 6, split: ['hello', ' 1 2', 'world'] },
// width = 0, impossible, so split into individual characters // width = 0, impossible, so split into individual characters
{ str: '🏳️‍⚧️🏳️‍🌈👩🏾‍❤️‍👨🏻', width: 0, split: ['🏳️‍⚧️', '🏳️‍🌈', '👩🏾‍❤️‍👨🏻'] }, { str: 'abc', width: 0, split: ['a', 'b', 'c'] },
{ str: '🏳️‍⚧️🏳️‍🌈👩🏾‍❤️‍👨🏻', width: 1, split: ['🏳️‍⚧️', '🏳️‍🌈', '👩🏾‍❤️‍👨🏻'] }, { str: '🏳️‍⚧️🏳️‍🌈👩🏾‍❤️‍👨🏻', width: 1, split: [...'🏳️‍⚧️🏳️‍🌈👩🏾‍❤️‍👨🏻'] },
{ str: '🏳️‍⚧️🏳️‍🌈👩🏾‍❤️‍👨🏻', width: 2, split: ['🏳️‍⚧️🏳️‍🌈', '👩🏾‍❤️‍👨🏻'] },
{ str: '🏳️‍⚧️🏳️‍🌈👩🏾‍❤️‍👨🏻', width: 3, split: ['🏳️‍⚧️🏳️‍🌈👩🏾‍❤️‍👨🏻'] },
{ str: '中文中', width: 1, split: ['中', '文', '中'] }, { str: '中文中', width: 1, split: ['中', '文', '中'] },
{ str: '中文中', width: 2, split: ['中文', '中'] }, { str: '中文中', width: 2, split: ['中文', '中'] },
{ str: '中文中', width: 3, split: ['中文中'] }, { str: '中文中', width: 3, split: ['中文中'] },
{ str: 'Flag 🏳️‍⚧️ this 🏳️‍🌈', width: 6, split: ['Flag 🏳️‍⚧️', 'this 🏳️‍🌈'] },
])( ])(
'should split $str into lines of $width characters', 'should split $str into lines of $width characters',
({ str, split, width }: { str: string; width: number; split: string[] }) => { ({ str, split, width }: { str: string; width: number; split: string[] }) => {
const checkFn = createCheckFn(width); const checkFn = createCheckFn(width);
const line: MarkdownLine = getLineFromString(str); const line: MarkdownLine = getLineFromString(str);
expect(splitLineToFitWidth(line, checkFn)).toEqual( expect(splitLineToFitWidth(line, checkFn)).toEqual(
split.map((str) => splitLineToWords(str).map((content) => ({ content, type: 'normal' }))) split.map((str) => getLineFromString(str))
); );
} }
); );
});
it('should handle strings with newlines', () => { it('should handle strings with newlines', () => {
const checkFn: CheckFitFunction = createCheckFn(6); const checkFn: CheckFitFunction = createCheckFn(6);
const str = `Flag const str = `Flag
🏳️‍⚧️ this 🏳️‍🌈`; 🏳️‍⚧️ this 🏳️‍🌈`;
expect(() => expect(() =>
splitLineToFitWidth(getLineFromString(str), checkFn) splitLineToFitWidth(getLineFromString(str), checkFn)
).toThrowErrorMatchingInlineSnapshot( ).toThrowErrorMatchingInlineSnapshot(
'"splitLineToFitWidth does not support newlines in the line"' '"splitLineToFitWidth does not support newlines in the line"'
); );
});
}); });
const getLineFromString = (str: string, type: MarkdownWordType = 'normal'): MarkdownLine => { const getLineFromString = (str: string, type: MarkdownWordType = 'normal'): MarkdownLine => {
@@ -91,3 +127,17 @@ const getLineFromString = (str: string, type: MarkdownWordType = 'normal'): Mark
type, type,
})); }));
}; };
/**
* Creates a checkFunction for a given width
* @param width - width of characters to fit in a line
* @returns checkFunction
*/
const createCheckFn = (width: number): CheckFitFunction => {
return (text: MarkdownLine) => {
// Join all words into a single string
const joinedContent = text.map((w) => w.content).join('');
const characters = splitTextToChars(joinedContent);
return characters.length <= width;
};
};

View File

@@ -22,7 +22,7 @@ export function splitLineToWords(text: string): string[] {
// Split by ' ' removes the ' 's from the result. // Split by ' ' removes the ' 's from the result.
const words = text.split(' '); const words = text.split(' ');
// Add the ' 's back to the result. // Add the ' 's back to the result.
const wordsWithSpaces = words.flatMap((s) => [s, ' ']); const wordsWithSpaces = words.flatMap((s) => [s, ' ']).filter((s) => s);
// Remove last space. // Remove last space.
wordsWithSpaces.pop(); wordsWithSpaces.pop();
return wordsWithSpaces; return wordsWithSpaces;