verse-reference-regex
Version:
A regular expression that matches Bible verse references and ranges
373 lines (346 loc) • 10.5 kB
JavaScript
const test = require('tape')
const { createRegex, extractRangeFromMatch, createChapterVerseRangeRegex } = require('./')
function buildTestSuite() {
const hasReferenceWithVerse = [
{
text: `Revelation 13:3`,
expected: {
book: 'Revelation',
start: { chapter: 13, verse: 3, section: null },
end: { chapter: 13, verse: 3, section: null },
},
}, {
text: `Look it up on Genesis 3:55`,
expected: {
book: 'Genesis',
start: { chapter: 3, verse: 55, section: null },
end: { chapter: 3, verse: 55, section: null },
},
}, {
text: `Test the Romans 44:559 out`,
expected: {
book: 'Romans',
start: { chapter: 44, verse: 559, section: null },
end: { chapter: 44, verse: 559, section: null },
},
}, {
text: `Low in \nthe philippians 366:4 he lay`,
expected: {
book: 'Philippians',
start: { chapter: 366, verse: 4, section: null },
end: { chapter: 366, verse: 4, section: null },
},
}, {
text: `An abbreviation is Ps 13:4 y'know`,
expected: {
book: 'Psalms',
start: { chapter: 13, verse: 4, section: null },
end: { chapter: 13, verse: 4, section: null },
},
}, {
text: `Another abbreviation is Ps. 44:8`,
expected: {
book: 'Psalms',
start: { chapter: 44, verse: 8, section: null },
end: { chapter: 44, verse: 8, section: null },
},
}, {
text: `Verse section: 1 sam 12:4b`,
expected: {
book: '1 Samuel',
start: { chapter: 12, verse: 4, section: 'b' },
end: { chapter: 12, verse: 4, section: 'b' },
},
}, {
text: `Second bookname: 2nd sam 12:4b`,
expected: {
book: '2 Samuel',
start: { chapter: 12, verse: 4, section: 'b' },
end: { chapter: 12, verse: 4, section: 'b' },
},
},
]
const hasReferenceWithoutVerse = [
{
text: `Revelation 13`,
expected: {
book: 'Revelation',
start: { chapter: 13, verse: null, section: null },
end: { chapter: 13, verse: null, section: null },
},
}, {
text: `Look it up on Genesis 3: book`,
expected: {
book: 'Genesis',
start: { chapter: 3, verse: null, section: null },
end: { chapter: 3, verse: null, section: null },
},
}, {
text: `Test the Romans 44 out`,
expected: {
book: 'Romans',
start: { chapter: 44, verse: null, section: null },
end: { chapter: 44, verse: null, section: null },
},
}, {
text: `Low in \nthe philippians 366 he lay`,
expected: {
book: 'Philippians',
start: { chapter: 366, verse: null, section: null },
end: { chapter: 366, verse: null, section: null },
},
}, {
text: `An abbreviation is Ps 13 y'know`,
expected: {
book: 'Psalms',
start: { chapter: 13, verse: null, section: null },
end: { chapter: 13, verse: null, section: null },
},
}, {
text: `Another abbreviation is Ps. 44`,
expected: {
book: 'Psalms',
start: { chapter: 44, verse: null, section: null },
end: { chapter: 44, verse: null, section: null },
},
},
]
const hasRangeWithVerse = [
{
text: `Revelation 13:3-14:4`,
expected: {
book: 'Revelation',
start: { chapter: 13, verse: 3, section: null },
end: { chapter: 14, verse: 4, section: null },
},
}, {
text: `Look it up on Genesis 3:55-23:44`,
expected: {
book: 'Genesis',
start: { chapter: 3, verse: 55, section: null },
end: { chapter: 23, verse: 44, section: null },
},
}, {
text: `Test the Romans 44:559-1:1 out`,
expected: {
book: 'Romans',
start: { chapter: 44, verse: 559, section: null },
end: { chapter: 1, verse: 1, section: null },
},
}, {
text: `Low in \nthe philippians 366:4-12:12 he lay`,
expected: {
book: 'Philippians',
start: { chapter: 366, verse: 4, section: null },
end: { chapter: 12, verse: 12, section: null },
},
}, {
text: `An abbreviation is Ps 13:4-19:19 y'know`,
expected: {
book: 'Psalms',
start: { chapter: 13, verse: 4, section: null },
end: { chapter: 19, verse: 19, section: null },
},
}, {
text: `Another abbreviation is Ps. 44:8-3:3`,
expected: {
book: 'Psalms',
start: { chapter: 44, verse: 8, section: null },
end: { chapter: 3, verse: 3, section: null },
},
}, {
text: `Verse section: 1 sam 12:4b-13:5a`,
expected: {
book: '1 Samuel',
start: { chapter: 12, verse: 4, section: 'b' },
end: { chapter: 13, verse: 5, section: 'a' },
},
},
{
text: `Revelation 13:3-4`,
expected: {
book: 'Revelation',
start: { chapter: 13, verse: 3, section: null },
end: { chapter: 13, verse: 4, section: null },
},
}, {
text: `Look it up on Genesis 3:55-44`,
expected: {
book: 'Genesis',
start: { chapter: 3, verse: 55, section: null },
end: { chapter: 3, verse: 44, section: null },
},
}, {
text: `Test the Romans 44:559-1 out`,
expected: {
book: 'Romans',
start: { chapter: 44, verse: 559, section: null },
end: { chapter: 44, verse: 1, section: null },
},
}, {
text: `Low in \nthe philippians 366:4-12 he lay`,
expected: {
book: 'Philippians',
start: { chapter: 366, verse: 4, section: null },
end: { chapter: 366, verse: 12, section: null },
},
}, {
text: `An abbreviation is Ps 13:4-19 y'know`,
expected: {
book: 'Psalms',
start: { chapter: 13, verse: 4, section: null },
end: { chapter: 13, verse: 19, section: null },
},
}, {
text: `Another abbreviation is Ps. 44:8-3`,
expected: {
book: 'Psalms',
start: { chapter: 44, verse: 8, section: null },
end: { chapter: 44, verse: 3, section: null },
},
}, {
text: `Verse section: 1 sam 12:4b-5a`,
expected: {
book: '1 Samuel',
start: { chapter: 12, verse: 4, section: 'b' },
end: { chapter: 12, verse: 5, section: 'a' },
},
},
{
text: `Verse section: 1 sam 12:4b-a`,
expected: {
book: '1 Samuel',
start: { chapter: 12, verse: 4, section: 'b' },
end: { chapter: 12, verse: 4, section: 'a' },
},
},
]
const hasRangeWithoutVerse = [
{
text: `Revelation 13-14`,
expected: {
book: 'Revelation',
start: { chapter: 13, verse: null, section: null },
end: { chapter: 14, verse: null, section: null },
},
}, {
text: `Look it up on Genesis 3-99 book`,
expected: {
book: 'Genesis',
start: { chapter: 3, verse: null, section: null },
end: { chapter: 99, verse: null, section: null },
},
}, {
text: `Test the Romans 44-45 out`,
expected: {
book: 'Romans',
start: { chapter: 44, verse: null, section: null },
end: { chapter: 45, verse: null, section: null },
},
}, {
text: `Low in \nthe philippians 366-78 he lay`,
expected: {
book: 'Philippians',
start: { chapter: 366, verse: null, section: null },
end: { chapter: 78, verse: null, section: null },
},
}, {
text: `An abbreviation is Ps 13-18 y'know`,
expected: {
book: 'Psalms',
start: { chapter: 13, verse: null, section: null },
end: { chapter: 18, verse: null, section: null },
},
}, {
text: `Another abbreviation is Ps. 44-49`,
expected: {
book: 'Psalms',
start: { chapter: 44, verse: null, section: null },
end: { chapter: 49, verse: null, section: null },
},
},
]
const hasNoReference = [
{
text: `Pumpkin pie`,
expected: null,
}, {
text: `Test the Romans out`,
expected: null,
}, {
text: `Low in \nthe philharmonic 366:4 he lay`,
expected: null,
}, {
text: `An abbreviation is Ps y'know 44:8`,
expected: null,
},
]
const hasVerse = [ ...hasReferenceWithVerse, ...hasRangeWithVerse ]
const hasNoVerse = [ ...hasNoReference, ...hasReferenceWithoutVerse, ...hasRangeWithoutVerse ]
const hasAnyReferenceOrRange = [ ...hasReferenceWithVerse, ...hasReferenceWithoutVerse, ...hasRangeWithVerse, ...hasRangeWithoutVerse ]
const tests = [
{ options: { requireVerse: true }, shouldMatch: hasVerse, shouldNotMatch: hasNoVerse },
{ options: { requireVerse: false }, shouldMatch: hasAnyReferenceOrRange, shouldNotMatch: hasNoReference },
]
return function runAllTests(description, testCaseTester) {
test(description, t => {
tests.forEach(({ options, shouldMatch, shouldNotMatch }) => {
t.test(`Testing options: ${JSON.stringify(options)}`, t => {
const regex = createRegex(options)
testCaseTester({ t, expectedToFindMatch: true, regex, cases: shouldMatch, options })
testCaseTester({ t, expectedToFindMatch: false, regex, cases: shouldNotMatch, options })
t.end()
})
})
t.end()
})
}
}
const runAllTests = buildTestSuite()
runAllTests('Matches', ({ t, expectedToFindMatch, regex, cases }) => {
const expectation = expectedToFindMatch ? 'Should' : 'Should not'
cases.forEach(({ text }) => {
t.equal(regex.test(text), expectedToFindMatch, `${expectation} match '${text}'`)
})
})
runAllTests('Capturing', ({ t, expectedToFindMatch, regex, cases }) => {
cases.forEach(({ text, expected }) => {
const match = text.match(regex)
if (expectedToFindMatch) {
const output = extractRangeFromMatch(match)
t.equal(output.book, expected.book, `Find book name in '${text}'`)
t.deepEqual(output.start, expected.start, `Find range start in '${text}'`)
t.deepEqual(output.end, expected.end, `Find range end in '${text}'`)
} else {
t.equal(match, null, `No match found in ${text}`)
}
})
})
test('Allow passing in custom book data structures', t => {
const books = [
{ name: 'Bob', aliases: [] },
]
const regex = createRegex({ requireVerse: true, books })
const noMatchText = `something something Genesis 10:10 something`
t.notOk(noMatchText.match(regex))
const text = `Tell me more about Bob 17:3 wouldja? Not though`
const match = text.match(regex)
t.ok(match)
const output = extractRangeFromMatch(match, books)
t.equal(output.book, 'Bob')
t.deepEqual(output.start, { chapter: 17, verse: 3, section: null })
t.deepEqual(output.end, { chapter: 17, verse: 3, section: null })
t.end()
})
test('Basic test for chapter-verse-range API', t => {
const chapterVerseRegex = createChapterVerseRangeRegex()
const chapterVerseMatch = `Tell me about 12:30-14:1a y'all`.match(chapterVerseRegex)
const output = extractRangeFromMatch.chapterVerseRange(chapterVerseMatch)
const expected = {
book: null,
start: { chapter: 12, verse: 30, section: null },
end: { chapter: 14, verse: 1, section: 'a' },
}
t.deepEqual(output, expected)
t.end()
})