88 lines
3.2 KiB
JavaScript
88 lines
3.2 KiB
JavaScript
const { MARKER_REGEX, parseMarker, MAX_MARKER_LEN } = require('../src/citations/markers');
|
|
const { createMarkerStripper } = require('../src/citations/markerStripper');
|
|
|
|
function consume(chunks) {
|
|
const stripper = createMarkerStripper();
|
|
let text = '';
|
|
const markers = [];
|
|
let malformed = 0;
|
|
for (const chunk of chunks) {
|
|
const step = stripper.push(chunk);
|
|
text += step.text;
|
|
markers.push(...step.markers);
|
|
malformed += step.malformed;
|
|
}
|
|
const tail = stripper.flush();
|
|
text += tail.text;
|
|
markers.push(...tail.markers);
|
|
malformed += tail.malformed;
|
|
return { text, markers, malformed };
|
|
}
|
|
|
|
function splitRandom(value, random) {
|
|
const chunks = [];
|
|
let index = 0;
|
|
while (index < value.length) {
|
|
const size = 1 + Math.floor(random() * 5);
|
|
chunks.push(value.slice(index, index + size));
|
|
index += size;
|
|
}
|
|
return chunks;
|
|
}
|
|
|
|
describe('marker grammar', () => {
|
|
it('parses pages, ranges, and chapters', () => {
|
|
expect(parseMarker('[[p:47]]')).toEqual({ kind: 'page', page: 47, pageEnd: 47 });
|
|
expect(parseMarker('[[P: 47 - 49]]')).toEqual({ kind: 'page', page: 47, pageEnd: 49 });
|
|
expect(parseMarker('[[ch:3]]')).toEqual({ kind: 'chapter', chapterNo: 3 });
|
|
expect(parseMarker('[[p:abc]]')).toBeNull();
|
|
expect(MAX_MARKER_LEN).toBe(24);
|
|
expect('see [[p:47]][[ch:3]]'.match(MARKER_REGEX)).toHaveLength(2);
|
|
});
|
|
});
|
|
|
|
describe('marker stripper', () => {
|
|
it('keeps the same text and markers for any chunk split', () => {
|
|
const source = 'Start [[p:47]] middle [[p:47-49]][[ch:3]] end.';
|
|
const whole = consume([source]);
|
|
let seed = 7;
|
|
const random = () => {
|
|
seed = (seed * 1103515245 + 12345) % 2147483648;
|
|
return seed / 2147483648;
|
|
};
|
|
for (let trial = 0; trial < 30; trial += 1) {
|
|
expect(consume(splitRandom(source, random))).toEqual(whole);
|
|
}
|
|
expect(whole.text).toBe('Start middle end.');
|
|
expect(whole.markers).toEqual([
|
|
{ kind: 'page', page: 47, pageEnd: 47 },
|
|
{ kind: 'page', page: 47, pageEnd: 49 },
|
|
{ kind: 'chapter', chapterNo: 3 },
|
|
]);
|
|
});
|
|
|
|
it('handles markers at the start, middle, and end', () => {
|
|
expect(consume(['[[ch:3]] Hello']).text).toBe('Hello');
|
|
expect(consume(['Hello [[p:2]] there']).text).toBe('Hello there');
|
|
expect(consume(['Hello [[p:2]].']).text).toBe('Hello.');
|
|
});
|
|
|
|
it('rejoins markers split across chunks', () => {
|
|
expect(consume(['[[', 'p:', '47]]']).markers).toEqual([{ kind: 'page', page: 47, pageEnd: 47 }]);
|
|
expect(consume(['[', '[p:47]]']).text).toBe('');
|
|
expect(consume(['See [', '[p:1]] now']).text).toBe('See now');
|
|
});
|
|
|
|
it('strips malformed markers and keeps ordinary brackets', () => {
|
|
const bad = consume(['Note [[p:abc]] next']);
|
|
expect(bad.text).toBe('Note next');
|
|
expect(bad.markers).toEqual([]);
|
|
expect(bad.malformed).toBe(1);
|
|
expect(consume(['[[p:4']).text).toBe('[[p:4');
|
|
expect(consume(['Read [text](url) please']).text).toBe('Read [text](url) please');
|
|
expect(consume(['Just [[ here']).text).toBe('Just [[ here');
|
|
expect(consume(['[[ not a marker at all and much longer than the hold']).text).toContain('[[');
|
|
expect(consume(['Café [[p:8]] résumé']).text).toBe('Café résumé');
|
|
});
|
|
});
|