doctest-ts/src/UnitTest.ts

80 lines
No EOL
2.3 KiB
TypeScript

import {Context} from "./ExtractComments";
import {ScriptExtraction} from "./ScriptExtraction";
export type Script = (Statement | Equality)[]
export interface Equality {
tag: '=='
lhs: string
rhs: string,
// Line number in the single script
line: number
}
export interface Statement {
tag: 'Statement'
stmt: string
}
/**
* Represents a single unit test somewhere in a file.
*/
export default class UnitTest {
public body: Script;
public context: Context;
private _extraImports: string[];
private constructor(body: Script, context: Context, extraImports: string[]) {
this.body = body;
this.context = context;
this._extraImports = extraImports;
}
public static FromComment(comment: string, context: Context): UnitTest[] {
const imports = ScriptExtraction.extractImports(comment)
return ScriptExtraction.extractScripts(comment).map(
({script, name}, i) =>
new UnitTest(script, {
...context,
linenumber: (context.linenumber ?? 0) ,
testname: name ?? 'doctest ' + i
},imports))
}
public static FromComments(comms: { comment: string, context: Context }[]): UnitTest[] {
const result: UnitTest[] = []
for (const comm of comms) {
result.push(...UnitTest.FromComment(comm.comment, comm.context))
}
return result
}
public getImports(): string[] {
return [...this._extraImports, 'import "mocha"', 'import {expect as __expect} from "chai"']
}
/**
* Generates the mocha test code for this unit test.
* Will only construct the 'it('should ....') { __expect(x).deep.eq(y) } part
*/
public generateCode() {
const script = this.body
.map(s => {
if (s.tag == 'Statement') {
return s.stmt
} else {
return `__expect(${s.lhs}, "failed at ${this.context.functionname} (${this.context.filepath}:${1 + s.line + (this.context.linenumber ?? 0)}:1)").to.deep.equal(${s.rhs})`
}
})
.map(x => '\n ' + x)
.join('')
return `it(${this.getName()}, () => {${script}\n})`
}
private getName() {
return JSON.stringify(this.context.testname ?? this.context.functionname)
}
}