versification
Version:
A library for parsing Paratext's vrs files.
499 lines (392 loc) • 18.7 kB
text/typescript
import {expect} from 'chai';
import Versification from '../src/Versification'
import * as testData from './testData.js';
import VerseRef from '../src/VerseRef';
describe('Versification', () =>
{
describe('Comment lines', () =>
{
it('single meaningless comment line', () =>
{
const v = new Versification('#hello world');
expect(v.name).to.be.empty;
expect(v.books()).to.be.empty;
});
it('single name comment line', () =>
{
const v = new Versification('# Versification "Septuagint"');
expect(v.name).to.be.equal('Septuagint');
});
it('single name comment line, with leading spaces', () =>
{
const v = new Versification(' # Versification "Septuagint"');
expect(v.name).to.be.equal('Septuagint');
});
it('multiple comment lines', () =>
{
const v = new Versification(`# Versification "Original"
# Version=1.200`);
expect(v.name).to.be.equal('Original');
});
it('duplicate name comment lines - second one is ignored', () =>
{
const v = new Versification(`# Versification "Original"
# Versification "Septuagint"`);
expect(v.name).to.be.equal('Original');
});
})
describe('chapter / verse definitions', () =>
{
it('Genesis line', () =>
{
const testLine = 'GEN 1:31 2:25 3:24 4:26 5:32 6:22';
const v = new Versification(testLine);
expect(v.books()).to.have.lengthOf(2);
expect(v.books()[0]).to.be.undefined;
expect(v.books()[1]).to.have.lengthOf(7);
expect(v.books()[1][0]).to.be.undefined;
expect(v.books()[1][1]).to.be.equal(31);
expect(v.books()[1][2]).to.be.equal(25);
})
it('Exodus line', () =>
{
const testLine = 'EXO 1:22 2:25 3:22 4:31';
const v = new Versification(testLine);
expect(v.books()).to.have.lengthOf(3);
expect(v.books()[0]).to.be.undefined;
expect(v.books()[1]).to.be.undefined;
expect(v.books()[2]).to.have.lengthOf(5);
expect(v.books()[2][0]).to.be.undefined;
expect(v.books()[2][1]).to.be.equal(22);
expect(v.books()[2][2]).to.be.equal(25);
})
it('Overrides - subsequent lines override previous lines', () =>
{
const testLine = `GEN 1:31 2:25 3:24 4:26 5:32 6:22
GEN 1:30`;
const v = new Versification(testLine);
expect(v.books()).to.have.lengthOf(2);
expect(v.books()[0]).to.be.undefined;
expect(v.books()[1]).to.have.lengthOf(7);
expect(v.books()[1][0]).to.be.undefined;
expect(v.books()[1][1]).to.be.equal(30);
expect(v.books()[1][2]).to.be.equal(25);
});
})
describe('standard mappings', () =>
{
it('single verse mapping', () =>
{
const testLine = `GEN 31:55 = GEN 32:1`;
const v = new Versification(testLine);
expect(v.mappings()).to.have.lengthOf(1);
expect(v.mappings()[0][0].toString()).to.be.equal('GEN 31:55');
expect(v.mappings()[0][1].toString()).to.be.equal('GEN 32:1');
});
it('whole chapter mapping', () =>
{
const testLine = `PSA 3:0-8 = PSA 3:1-9`;
const v = new Versification(testLine);
expect(v.mappings()).to.have.lengthOf(9);
expect(v.mappings()[0][0].toString()).to.be.equal('PSA 3:0');
expect(v.mappings()[0][1].toString()).to.be.equal('PSA 3:1');
expect(v.mappings()[8][0].toString()).to.be.equal('PSA 3:8');
expect(v.mappings()[8][1].toString()).to.be.equal('PSA 3:9');
});
it('invalid reverse range mapping - ignores the range part', () =>
{
const testLine = `PSA 3:0-2 = PSA 3:2-0`;
const v = new Versification(testLine);
expect(v.mappings()).to.have.lengthOf(3);
expect(v.mappings()[0][0].toString()).to.be.equal('PSA 3:0');
expect(v.mappings()[0][1].toString()).to.be.equal('PSA 3:2');
expect(v.mappings()[1][0].toString()).to.be.equal('PSA 3:1');
expect(v.mappings()[1][1].toString()).to.be.equal('PSA 3:2');
expect(v.mappings()[2][0].toString()).to.be.equal('PSA 3:2');
expect(v.mappings()[2][1].toString()).to.be.equal('PSA 3:2');
});
});
describe('one to many mapping', () =>
{
it('two verse references -> one verse reference', () =>
{
const testLine = `#! &ACT 19:40-41 = ACT 19:40`;
const v = new Versification(testLine);
expect(v.mappings()).to.have.lengthOf(2);
expect(v.mappings()[0][0].toString()).to.be.equal('ACT 19:40');
expect(v.mappings()[0][1].toString()).to.be.equal('ACT 19:40');
expect(v.mappings()[1][0].toString()).to.be.equal('ACT 19:41');
expect(v.mappings()[1][1].toString()).to.be.equal('ACT 19:40');
});
});
describe('excluded verses', () =>
{
it('no excluded verses', () =>
{
const v = new Versification('');
expect(v.excludedVerses()).to.have.lengthOf(0);
expect(v.excludedVerses(1)).to.have.lengthOf(0);
expect(v.excludedVerses(1, 1)).to.have.lengthOf(0);
});
it('Single excluded verse', () =>
{
const testLine = `-EXO 25:6`;
const v = new Versification(testLine);
expect(v.excludedVerses()).to.have.lengthOf(1);
expect(v.excludedVerses()).to.contain(2025006);
});
it('multiple excluded verses', () =>
{
const testLine = `-EXO 25:6
-MAT 1:1`;
const v = new Versification(testLine);
expect(v.excludedVerses()).to.have.lengthOf(2);
expect(v.excludedVerses()).to.contain(2025006);
expect(v.excludedVerses()).to.contain(40001001);
});
it('duplicate excluded verses', () =>
{
const testLine = `-EXO 25:6
-EXO 25:6`;
const v = new Versification(testLine);
expect(v.excludedVerses()).to.have.lengthOf(1);
expect(v.excludedVerses()).to.contain(2025006);
});
it('invalid bookcode data - ignored', () =>
{
const testLine = `-bookcodedoesnotexist 25:99`;
const v = new Versification(testLine);
expect(v.excludedVerses()).to.have.lengthOf(0);
});
it('filtered by book', () =>
{
const testLine = `-EXO 25:6
-MAT 1:1`;
const v = new Versification(testLine);
expect(v.excludedVerses(2)).to.have.lengthOf(1);
expect(v.excludedVerses(40)).to.have.lengthOf(1);
expect(v.excludedVerses(3)).to.have.lengthOf(0);
expect(v.excludedVerses(2)).to.contain(2025006);
expect(v.excludedVerses(40)).to.contain(40001001);
});
it('filtered by book and chapter', () =>
{
const testLine = `-EXO 25:6
-MAT 1:1`;
const v = new Versification(testLine);
expect(v.excludedVerses(2, 25)).to.have.lengthOf(1);
expect(v.excludedVerses(2, 24)).to.have.lengthOf(0);
expect(v.excludedVerses(40, 1)).to.have.lengthOf(1);
expect(v.excludedVerses(40, 2)).to.have.lengthOf(0);
expect(v.excludedVerses(2, 25)).to.contain(2025006);
expect(v.excludedVerses(40, 1)).to.contain(40001001);
});
});
describe('verse segment', () =>
{
it('no verse segments', () =>
{
const v = new Versification('');
expect(Object.keys(v.verseSegments())).to.have.lengthOf(0);
expect(Object.keys(v.verseSegments(1))).to.have.lengthOf(0);
expect(Object.keys(v.verseSegments(1, 1))).to.have.lengthOf(0);
});
it('single verses segmented', () =>
{
// This line means that for Gen 9:2 the following exists:
// \v 2, \v 2a, \v 2b, \v 2c, ...
// If there was no '-' then, just \v 2a, \v 2b, \v 2c, ... would exist.
const testLine = `#! *GEN 9:2,-,a,b,c,d,e,f`;
const v = new Versification(testLine);
expect(Object.keys(v.verseSegments())).to.have.lengthOf(1);
expect(v.verseSegments()[1009002]).to.be.eql(['', 'a', 'b', 'c', 'd', 'e', 'f']);
});
it('verse segment, unknown book - ignored', () =>
{
const testLine = `#! *abc 9:2,-,a,b,c,d,e,f`;
const v = new Versification(testLine);
expect(Object.keys(v.verseSegments())).to.have.lengthOf(0);
});
it('filtered by book', () =>
{
const testLine = `#! *GEN 9:2,-,a,b,c,d,e,f`;
const v = new Versification(testLine);
expect(Object.keys(v.verseSegments(1))).to.have.lengthOf(1);
expect(Object.keys(v.verseSegments(40))).to.have.lengthOf(0);
expect(v.verseSegments(1)[1009002]).to.be.eql(['', 'a', 'b', 'c', 'd', 'e', 'f']);
});
it('filtered by book and chapter', () =>
{
const testLine = `#! *GEN 9:2,-,a,b,c,d,e,f`;
const v = new Versification(testLine);
expect(Object.keys(v.verseSegments(1, 9))).to.have.lengthOf(1);
expect(Object.keys(v.verseSegments(1, 10))).to.have.lengthOf(0);
expect(v.verseSegments(1, 9)[1009002]).to.be.eql(['', 'a', 'b', 'c', 'd', 'e', 'f']);
});
});
describe('sample data', () =>
{
it('original sample', () =>
{
const v = new Versification(testData.original);
expect(v.books()).to.have.lengthOf(41, "MAT is the last book in sample data");
expect(v.books()[40]).to.have.lengthOf(29, "28 chapters (1 based index)");
});
it('english sample', () =>
{
const v = new Versification(testData.eng);
expect(v.books()).to.have.lengthOf(41, "MAT is the last book in sample data");
expect(v.books()[40]).to.have.lengthOf(29, "28 chapters (1 based index)");
expect(v.mappings()).to.have.lengthOf(1151)
});
});
describe('changeVersification', () =>
{
it('PSA 3:2 with undefined versification to original', () =>
{
const originalVersification = new Versification(testData.original);
const verseRef = VerseRef.parse("PSA 3:2");
// @ts-ignore - verseRef won't be undefined.
const updatedVerseRef = originalVersification.changeVersification(verseRef);
expect(updatedVerseRef?.toString()).to.be.equal('PSA 3:2');
expect(updatedVerseRef.versification()).to.be.equal(originalVersification);
})
it('PSA 3:2 English to Original - Changes to PSA 3:3 (as PSA 3 heading counts a verse 1)', () =>
{
const englishVersification = new Versification(testData.eng);
const originalVersification = new Versification(testData.original);
const verseRef = VerseRef.parse("PSA 3:2", englishVersification);
expect(verseRef).to.not.be.null;
// @ts-ignore - verseRef won't be undefined.
const updatedVerseRef = originalVersification.changeVersification(verseRef);
expect(updatedVerseRef?.toString()).to.be.equal('PSA 3:3');
expect(verseRef?.toString()).to.be.equal('PSA 3:2');
});
it('PSA 3:3 Original to English - Changes to PSA 3:2', () =>
{
const englishVersification = new Versification(testData.eng);
const originalVersification = new Versification(testData.original);
const verseRef = VerseRef.parse("PSA 3:3", originalVersification);
expect(verseRef).to.not.be.null;
// @ts-ignore - verseRef won't be undefined.
const updatedVerseRef = englishVersification.changeVersification(verseRef);
expect(updatedVerseRef?.toString()).to.be.equal('PSA 3:2');
expect(verseRef?.toString()).to.be.equal('PSA 3:3');
});
it('PSA 3:2 English to Custom (Which matches English versification for PSA 3)', () =>
{
const englishVersification = new Versification(testData.eng);
const customVersification = new Versification(testData.myCustom);
const verseRef = VerseRef.parse("PSA 3:2", englishVersification);
expect(verseRef).to.not.be.null;
// @ts-ignore - verseRef won't be undefined.
const updatedVerseRef = customVersification.changeVersification(verseRef);
expect(updatedVerseRef?.toString()).to.be.equal('PSA 3:2');
expect(verseRef?.toString()).to.be.equal('PSA 3:2');
});
it('PSA 3:2-3 English to Original - changes to PSA 3:3-4', () =>
{
const englishVersification = new Versification(testData.eng);
const originalVersification = new Versification(testData.original);
const verseRef = VerseRef.parse("PSA 3:2-3", englishVersification);
expect(verseRef).to.not.be.null;
// @ts-ignore - verseRef won't be undefined.
const updatedVerseRef = originalVersification.changeVersification(verseRef);
expect(updatedVerseRef?.toString()).to.be.equal('PSA 3:3-4');
expect(updatedVerseRef?.versification().name).to.be.equal(originalVersification.name);
expect(verseRef?.toString()).to.be.equal('PSA 3:2-3');
});
it('PSA 3:3-4 Original to English - changes to PSA 3:2-3', () =>
{
const englishVersification = new Versification(testData.eng);
const originalVersification = new Versification(testData.original);
const verseRef = VerseRef.parse("PSA 3:3-4", originalVersification);
expect(verseRef).to.not.be.null;
// @ts-ignore - verseRef won't be undefined.
const updatedVerseRef = englishVersification.changeVersification(verseRef);
expect(updatedVerseRef?.toString()).to.be.equal('PSA 3:2-3');
expect(updatedVerseRef?.versification().name).to.be.equal(englishVersification.name);
expect(verseRef?.toString()).to.be.equal('PSA 3:3-4');
});
it('MAT 2:6 English to LXX (I know this does not make sense :) ) (regression test)', () =>
{
const englishVersification = new Versification(testData.eng);
const lxxVersification = new Versification(testData.lxx);
const verseRef = VerseRef.parse("MAT 2:6", englishVersification);
expect(verseRef).to.not.be.null;
// @ts-ignore - verseRef won't be undefined.
const updatedVerseRef = lxxVersification.changeVersification(verseRef);
expect(updatedVerseRef?.toString()).to.be.equal('MAT 2:6');
});
it('PSA 151:1 LXX to English - does not exist in target project', () =>
{
const englishVersification = new Versification(testData.eng);
const lxxVersification = new Versification(testData.lxx);
const verseRef = VerseRef.parse("PSA 151:1", lxxVersification);
expect(verseRef).to.not.be.null;
// @ts-ignore - verseRef won't be undefined.
const updatedVerseRef = englishVersification.changeVersification(verseRef);
expect(updatedVerseRef?.toString()).to.be.equal('PSA 151:1');
});
it('English to English - returns same verseRef instance', () =>
{
const englishVersification = new Versification(testData.eng);
const verseRef = VerseRef.parse("MAT 2:6", englishVersification);
// @ts-ignore - verseRef won't be undefined.
const newVerseRef = englishVersification.changeVersification(verseRef);
expect(verseRef).to.be.equal(newVerseRef, "Should be same instance as versification was the same");
})
it('null - returns null', () =>
{
const englishVersification = new Versification(testData.eng);
// @ts-ignore - testing non typesafe invokes (js)
expect(englishVersification.changeVersification(null)).to.be.null;
});
});
describe('nameToFileName', () =>
{
[
['English', 'eng'],
['English-f421fe261da7624f0405a602838bbed467f5fdb2abcdefff', 'eng'],
['Original-3f0f2b0426e1457e8e496834aaa30fce00000002abcdefff', 'org'],
['Septuagint', 'lxx'],
['Russian Protestant', 'rsc'],
['RussianProtestant-64f96631883240818dc01c345e58a1bff75e18e1abcdefff', 'rsc'],
['Vulgate', 'vul'],
].forEach(x =>
it(`${x[0]} => ${x[1]}`, () =>
{
expect(Versification.nameToFileName(x[0])).to.be.equal(x[1]);
}));
});
describe('bookIdToNumber', () =>
{
[
['GEN', 1],
['MAT', 40]
].forEach(x =>
it(`${x[0]} => ${x[1]}`, () =>
{
expect(Versification.bookIdToNumber('' + x[0])).to.be.equal(x[1]);
}));
});
describe('equals', () =>
{
it('same name - considered equal', () =>
{
const a = new Versification(`# Versification "Original"
# Version=1.200`);
const b = new Versification(`# Versification "Original"
# Version=1.200`);
expect(a.equals(b)).to.true;
});
it('different name - considered different', () =>
{
const a = new Versification(`# Versification "Original"
# Version=1.200`);
const b = new Versification(`# Versification "English"
# Version=1.200`);
expect(a.equals(b)).to.false;
});
});
});