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(``) }) })