Terrace/packages/js/src/parser.test.ts
2023-02-07 13:33:23 -05:00

254 lines
8.8 KiB
TypeScript

import { describe, expect, it } from 'vitest'
import { createLineData, parseLine } from './parser'
describe(`LineData`, () => {
it(`is an object`, () => {
const lineData = createLineData()
expect(lineData).toBeTypeOf(`object`)
})
it(`has five properties`, () => {
const lineData = createLineData()
expect(Object.keys(lineData).length).to.equal(5)
})
it(`'line' is a string|null initialized to null`, () => {
const lineData = createLineData()
expect(lineData.level).to.equal(0)
})
it(`'level' is an integer initialized to zero`, () => {
const lineData = createLineData()
expect(lineData.level).to.equal(0)
})
it(`'offsetHead' is an integer initialized to zero`, () => {
const lineData = createLineData()
expect(lineData.offsetHead).to.equal(0)
})
it(`'offsetTail' is an integer initialized to zero`, () => {
const lineData = createLineData()
expect(lineData.offsetTail).to.equal(0)
})
})
describe(`parseLine`, () => {
it(`Requres 'lineData' to be an object with string line and numeric level properties`, () => {
// @ts-ignore
expect(() => parseLine(``, 0)).toThrowError(`'lineData' must be an object with string line and numeric level properties`)
// @ts-ignore
expect(() => parseLine(``, [])).toThrowError(`'lineData' must be an object with string line and numeric level properties`)
// @ts-ignore
expect(() => parseLine(``, {})).toThrowError(`'lineData' must be an object with string line and numeric level properties`)
// @ts-ignore
expect(() => parseLine(``, null)).toThrowError(`'lineData' must be an object with string line and numeric level properties`)
// @ts-ignore
expect(() => parseLine(``, true)).toThrowError(`'lineData' must be an object with string line and numeric level properties`)
// @ts-ignore
expect(() => parseLine(``, () => {})).toThrowError(`'lineData' must be an object with string line and numeric level properties`)
// @ts-ignore
expect(() => parseLine(``, { line: '', level: '' })).toThrowError(`'lineData' must be an object with string line and numeric level properties`)
// @ts-ignore
expect(() => parseLine(``, { line: '', level: 0 })).toThrowError(`'lineData' must be an object with string line and numeric level properties`)
})
it(`Requres 'indent' to be a single-character string`, () => {
const lineData = createLineData()
lineData.line = ``
// @ts-ignore
lineData.indent = 0
expect(() => parseLine(lineData)).toThrowError(`'lineData.indent' must be a single-character string`)
// @ts-ignore
lineData.indent = []
expect(() => parseLine(lineData)).toThrowError(`'lineData.indent' must be a single-character string`)
// @ts-ignore
lineData.indent = {}
expect(() => parseLine(lineData)).toThrowError(`'lineData.indent' must be a single-character string`)
// @ts-ignore
lineData.indent = null
expect(() => parseLine(lineData)).toThrowError(`'lineData.indent' must be a single-character string`)
// @ts-ignore
lineData.indent = true
expect(() => parseLine(lineData)).toThrowError(`'lineData.indent' must be a single-character string`)
// @ts-ignore
lineData.indent = () => {}
expect(() => parseLine(lineData)).toThrowError(`'lineData.indent' must be a single-character string`)
lineData.indent = ` `
expect(() => parseLine(lineData)).toThrowError(`'lineData.indent' must be a single-character string`)
})
it(`Handles a blank line at indent level 0`, () => {
const line = ``
const lineData = createLineData(line)
parseLine(lineData)
expect(lineData).to.deep.equal({ line, indent: ` `, level: 0, offsetHead: 0, offsetTail: 0 })
})
it(`Handles a line with a single space at indent level 1`, () => {
const line = ` `
const lineData = createLineData(line)
parseLine(lineData)
expect(lineData).to.deep.equal({ line, indent: ` `, level: 1, offsetHead: 1, offsetTail: 1 })
})
it(`Handles a line with two spaces`, () => {
const line = ` `
const lineData = createLineData(line)
parseLine(lineData)
expect(lineData).to.deep.equal({ line, indent: ` `, level: 2, offsetHead: 2, offsetTail: 2 })
})
it(`Handles a normal line at indent level 0`, () => {
const line = `line 1`
const lineData = createLineData(line)
parseLine(lineData)
expect(lineData).to.deep.equal({ line, indent: ` `, level: 0, offsetHead: 0, offsetTail: 4 })
})
it(`Handles a normal line at indent level 1`, () => {
const line = ` line 1`
const lineData = createLineData(line)
parseLine(lineData)
expect(lineData).to.deep.equal({ line, indent: ` `, level: 1, offsetHead: 1, offsetTail: 5 })
})
it(`Handles a normal line at indent level 2`, () => {
const line = ` line 1`
const lineData = createLineData(line)
parseLine(lineData)
expect(lineData).to.deep.equal({ line, indent: ` `, level: 2, offsetHead: 2, offsetTail: 6 })
})
it(`Handles a normal line at indent level 1 indented with tabs`, () => {
const line = `\tline 1`
const lineData = createLineData(line, `\t`)
parseLine(lineData)
expect(lineData).to.deep.equal({ line, indent: `\t`, level: 1, offsetHead: 1, offsetTail: 5 })
})
it(`Handles a normal line at indent level 2 indented with tabs`, () => {
const line = `\t\tline 1`
const lineData = createLineData(line, `\t`)
parseLine(lineData)
expect(lineData).to.deep.equal({ line, indent: `\t`, level: 2, offsetHead: 2, offsetTail: 6})
})
it(`Nests a normal line under a preceding normal line`, () => {
const lines = [
'line 1',
' line 2'
]
const lineData = createLineData()
const results = lines.map(line => {
lineData.line = line
parseLine(lineData)
return {...lineData}
})
expect(results).to.deep.equal([
{ line: lines[0], indent: ' ', level: 0, offsetHead: 0, offsetTail: 4 },
{ line: lines[1], indent: ' ', level: 1, offsetHead: 1, offsetTail: 5 }
])
})
it(`Nests multiple normal line under a preceding normal line`, () => {
const lines = [
'line 1',
' line 2',
' line 3',
' line 4',
]
const lineData = createLineData()
const results = lines.map(line => {
lineData.line = line
parseLine(lineData)
return {...lineData}
})
expect(results).to.deep.equal([
{ line: lines[0], indent: ' ', level: 0, offsetHead: 0, offsetTail: 4 },
{ line: lines[1], indent: ' ', level: 1, offsetHead: 1, offsetTail: 5 },
{ line: lines[2], indent: ' ', level: 1, offsetHead: 1, offsetTail: 5 },
{ line: lines[3], indent: ' ', level: 1, offsetHead: 1, offsetTail: 5 }
])
})
it(`Does not nest an empty line under a preceding normal line`, () => {
const lines = [
'line 1',
''
]
const lineData = createLineData()
const results = lines.map(line => {
lineData.line = line
parseLine(lineData)
return {...lineData}
})
expect(results).to.deep.equal([
{ line: lines[0], indent: ' ', level: 0, offsetHead: 0, offsetTail: 4 },
{ line: lines[1], indent: ' ', level: 0, offsetHead: 0, offsetTail: 0 }
])
})
it(`Does not nest multiple empty lines under a preceding normal line`, () => {
const lines = [
'line 1',
'',
'',
'',
]
const lineData = createLineData()
const results = lines.map(line => {
lineData.line = line
parseLine(lineData)
return {...lineData}
})
expect(results).to.deep.equal([
{ line: lines[0], indent: ' ', level: 0, offsetHead: 0, offsetTail: 4 },
{ line: lines[1], indent: ' ', level: 0, offsetHead: 0, offsetTail: 0 },
{ line: lines[2], indent: ' ', level: 0, offsetHead: 0, offsetTail: 0 },
{ line: lines[3], indent: ' ', level: 0, offsetHead: 0, offsetTail: 0 }
])
})
it(`Handle head and tail matching for lines with head and tail`, () => {
const line = ` head tail1 tail2 tail3`
const lineData = createLineData(line)
parseLine(lineData)
const head = line.slice(lineData.offsetHead, lineData.offsetTail)
const tail = line.slice(lineData.offsetTail + 1)
expect(head).to.equal(`head`)
expect(tail).to.equal(`tail1 tail2 tail3`)
})
it(`Handle head and tail matching for lines with head but no tail`, () => {
const line = ` head`
const lineData = createLineData(line)
parseLine(lineData)
const head = line.slice(lineData.offsetHead, lineData.offsetTail)
const tail = line.slice(lineData.offsetTail + 1)
expect(head).to.equal(`head`)
expect(tail).to.equal(``)
})
it(`Handle head and tail matching for lines with head and trailing space`, () => {
const line = ` head `
const lineData = createLineData(line)
parseLine(lineData)
const head = line.slice(lineData.offsetHead, lineData.offsetTail)
const tail = line.slice(lineData.offsetTail + 1)
expect(head).to.equal(`head`)
expect(tail).to.equal(``)
})
})