Last active
August 19, 2021 18:34
-
-
Save wonderful-panda/fbd7c6c848f938668a81bce18799ece8 to your computer and use it in GitHub Desktop.
[TypeScript] 期待した通りのコンパイルエラーが出るかどうかテストするやつ
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* | |
* 1. ../tsconfig.json に設定ファイルを置く | |
* 2. ../cases/ の中に example.ts みたいなファイルを置く (/// TSXXXX: ~ でその行で期待するコンパイルを記述する。~の部分は正規表現) | |
* 3. _runner.ts を ava で実行すると、出力サンプルみたいな感じで結果が出力される | |
*/ | |
import * as ts from "typescript/lib/typescript"; | |
import * as fs from "fs"; | |
import * as path from "path"; | |
import * as glob from "glob"; | |
import test from "ava"; | |
interface ExpectedError { | |
code: string; | |
message?: RegExp; | |
} | |
interface ActualError { | |
code: string; | |
message?: string; | |
} | |
const NoError = { code: "<no error>" }; | |
function getExpectedErrors(file: string): ExpectedError[] { | |
const lines = fs.readFileSync(file).toString().split(/\r?\n/); | |
const ret: ExpectedError[] = []; | |
lines.forEach((line, n) => { | |
const match = /\/\/\/\s*(TS[0-9]+)\s*:\s*(.*)$/.exec(line); | |
if (match) { | |
ret[n + 1] = { code: match[1], message: new RegExp(match[2]) }; | |
} | |
}); | |
return ret; | |
} | |
function getFailureMessage(line: number, expected: ExpectedError, actual: ActualError) { | |
const expectedString = expected.message ? `${ expected.code }: ${ expected.message }` : expected.code; | |
const actualString = actual.message ? `${ actual.code }: ${ actual.message }` : actual.code; | |
return ( | |
`At line ${line} | |
........ ----------- | |
........ [expected] | |
........ ${ expectedString.replace(/^/gm, " ") } | |
........ [actual] | |
........ ${ actualString.replace(/^/gm, " ") } | |
........ `.replace(/^\.+ /gm, "")); | |
} | |
const baseDir = path.dirname(__dirname); | |
const configPath = path.join(baseDir, "tsconfig.json"); | |
const configContent = fs.readFileSync(configPath).toString(); | |
const parsed = ts.parseJsonConfigFileContent(JSON.parse(configContent), ts.sys, baseDir); | |
const cases = glob.sync(path.join(baseDir, "cases/*.ts")); | |
const host: ts.LanguageServiceHost = { | |
getScriptFileNames: () => cases, | |
getScriptVersion: f => "0", | |
getScriptSnapshot: f => { | |
if (!fs.existsSync(f)) { | |
return undefined; | |
} | |
return ts.ScriptSnapshot.fromString(fs.readFileSync(f).toString()); | |
}, | |
getCurrentDirectory: () => process.cwd(), | |
getCompilationSettings: () => parsed.options, | |
getDefaultLibFileName: options => ts.getDefaultLibFileName(options) | |
} | |
const service = ts.createLanguageService(host, ts.createDocumentRegistry()); | |
cases.forEach(f => { | |
const filename = path.basename(f); | |
test(filename, t => { | |
const expectedErrors = getExpectedErrors(f); | |
const output = service.getEmitOutput(f); | |
const errors: ActualError[] = []; | |
service.getSemanticDiagnostics(f).forEach(d => { | |
const { line, character } = d.file.getLineAndCharacterOfPosition(d.start); | |
const message = ts.flattenDiagnosticMessageText(d.messageText, "\n"); | |
errors[line + 1] = { code: `TS${d.code}`, message }; | |
}); | |
const msg: string[] = []; | |
for (let i = 0; i < expectedErrors.length || i < errors.length; ++i) { | |
const expected = expectedErrors[i] || NoError; | |
const actual = errors[i] || NoError; | |
if (expected.code !== actual.code) { | |
msg.push(getFailureMessage(i, expected, actual)); | |
} | |
else if (expected.message && actual.message && !expected.message.test(actual.message)) { | |
msg.push(getFailureMessage(i, expected, actual)); | |
} | |
} | |
if (msg.length > 0) { | |
msg.push(`> ${msg.length} failures are found.`) | |
t.fail("\n" + msg.join("\n")); | |
} | |
}); | |
}); |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
interface Foo { | |
foo: string; | |
bar: number; | |
} | |
function testfunc(foo: Foo) { /// TS9999: unknown error | |
console.log(foo.fooo); /// TS2339: Property 'fooo' does not exist | |
} | |
testfunc({ foo: true }); /// TS2345: Property 'bar' is missing in |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
× example.ts | |
At line 6 | |
----------- | |
[expected] | |
TS9999: /unknown error/ | |
[actual] | |
<no error> | |
At line 10 | |
----------- | |
[expected] | |
TS2345: /Property 'bar' is missing in/ | |
[actual] | |
TS2345: Argument of type '{ foo: boolean; }' is not assignable to parameter of type 'Foo'. | |
Types of property 'foo' are incompatible. | |
Type 'boolean' is not assignable to type 'string'. | |
> 2 failures are found. | |
1 test failed [20:24:04] |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment