diff --git a/readme.md b/readme.md index 9f13621a..5e1dcf9a 100644 --- a/readme.md +++ b/readme.md @@ -213,6 +213,19 @@ Default: `process.cwd()` Current working directory of the project to retrieve the diagnostics for. +##### typingsFile + +Type: `string`
+Default: The `types` property in `package.json`. + +Path to the type definition file you want to test. This can be useful when using a test runner to test specific type definitions per test. + +##### testFiles + +Type: `string[]`
+Default: Finds files with `.test-d.ts` or `.test-d.tsx` extension. + +An array of test files with their path. Uses [globby](https://github.com/sindresorhus/globby) under the hood so that you can fine tune test file discovery. ## License diff --git a/source/lib/compiler.ts b/source/lib/compiler.ts index 07d67582..321d9082 100644 --- a/source/lib/compiler.ts +++ b/source/lib/compiler.ts @@ -1,4 +1,3 @@ -import * as path from 'path'; import { flattenDiagnosticMessageText, createProgram, @@ -65,11 +64,9 @@ const ignoreDiagnostic = (diagnostic: TSDiagnostic, expectedErrors: Map { - const fileNames = context.testFiles.map(fileName => path.join(context.cwd, fileName)); - const diagnostics: Diagnostic[] = []; - const program = createProgram(fileNames, context.config.compilerOptions); + const program = createProgram(context.testFiles, context.config.compilerOptions); const tsDiagnostics = program .getSemanticDiagnostics() diff --git a/source/lib/index.ts b/source/lib/index.ts index 4e56dbb2..5be84b42 100644 --- a/source/lib/index.ts +++ b/source/lib/index.ts @@ -9,11 +9,12 @@ import {Context, Config} from './interfaces'; export interface Options { cwd: string; + typingsFile?: string; + testFiles?: readonly string[]; } const findTypingsFile = async (pkg: any, options: Options) => { - const typings = pkg.types || pkg.typings || 'index.d.ts'; - + const typings = options.typingsFile || pkg.types || pkg.typings || 'index.d.ts'; const typingsExist = await pathExists(path.join(options.cwd, typings)); if (!typingsExist) { @@ -23,13 +24,37 @@ const findTypingsFile = async (pkg: any, options: Options) => { return typings; }; -const findTestFiles = async (typingsFile: string, options: Options & {config: Config}) => { +const normalizeTypingsFilePath = (typingsFilePath: string, options: Options) => { + if (options.typingsFile) { + return path.basename(typingsFilePath); + } + + return typingsFilePath; +}; + +const findCustomTestFiles = async (testFilesPattern: readonly string[], cwd: string) => { + const testFiles = await globby(testFilesPattern, {cwd}); + + if (testFiles.length === 0) { + throw new Error('Could not find any test files. Create one and try again'); + } + + return testFiles.map(file => path.join(cwd, file)); +}; + +const findTestFiles = async (typingsFilePath: string, options: Options & {config: Config}) => { + if (options.testFiles?.length) { + return findCustomTestFiles(options.testFiles, options.cwd); + } + + // Return only the filename if the `typingsFile` option is used. + const typingsFile = normalizeTypingsFilePath(typingsFilePath, options); + const testFile = typingsFile.replace(/\.d\.ts$/, '.test-d.ts'); const tsxTestFile = typingsFile.replace(/\.d\.ts$/, '.test-d.tsx'); const testDir = options.config.directory; let testFiles = await globby([testFile, tsxTestFile], {cwd: options.cwd}); - const testDirExists = await pathExists(path.join(options.cwd, testDir)); if (testFiles.length === 0 && !testDirExists) { @@ -40,7 +65,7 @@ const findTestFiles = async (typingsFile: string, options: Options & {config: Co testFiles = await globby([`${testDir}/**/*.ts`, `${testDir}/**/*.tsx`], {cwd: options.cwd}); } - return testFiles; + return testFiles.map(fileName => path.join(options.cwd, fileName)); }; /** @@ -56,7 +81,6 @@ export default async (options: Options = {cwd: process.cwd()}) => { } const pkg = pkgResult.packageJson; - const config = loadConfig(pkg as any, options.cwd); // Look for a typings file, otherwise use `index.d.ts` in the root directory. If the file is not found, throw an error. diff --git a/source/test/fixtures/specify-test-files/index.d.ts b/source/test/fixtures/specify-test-files/index.d.ts new file mode 100644 index 00000000..0616ebaa --- /dev/null +++ b/source/test/fixtures/specify-test-files/index.d.ts @@ -0,0 +1,6 @@ +declare const one: { + (foo: string, bar: string): string; + (foo: number, bar: number): number; +}; + +export default one; diff --git a/source/test/fixtures/specify-test-files/index.js b/source/test/fixtures/specify-test-files/index.js new file mode 100644 index 00000000..f17717f5 --- /dev/null +++ b/source/test/fixtures/specify-test-files/index.js @@ -0,0 +1,3 @@ +module.exports.default = (foo, bar) => { + return foo + bar; +}; diff --git a/source/test/fixtures/specify-test-files/package.json b/source/test/fixtures/specify-test-files/package.json new file mode 100644 index 00000000..de6dc1db --- /dev/null +++ b/source/test/fixtures/specify-test-files/package.json @@ -0,0 +1,3 @@ +{ + "name": "foo" +} diff --git a/source/test/fixtures/specify-test-files/unknown.test.ts b/source/test/fixtures/specify-test-files/unknown.test.ts new file mode 100644 index 00000000..080ee4ca --- /dev/null +++ b/source/test/fixtures/specify-test-files/unknown.test.ts @@ -0,0 +1,5 @@ +import {expectType} from '../../..'; +import one from '.'; + +expectType(one('foo', 'bar')); +expectType(one(1, 2)); diff --git a/source/test/fixtures/typings-custom-dir/index.js b/source/test/fixtures/typings-custom-dir/index.js new file mode 100644 index 00000000..f17717f5 --- /dev/null +++ b/source/test/fixtures/typings-custom-dir/index.js @@ -0,0 +1,3 @@ +module.exports.default = (foo, bar) => { + return foo + bar; +}; diff --git a/source/test/fixtures/typings-custom-dir/index.test-d.ts b/source/test/fixtures/typings-custom-dir/index.test-d.ts new file mode 100644 index 00000000..a6fedd96 --- /dev/null +++ b/source/test/fixtures/typings-custom-dir/index.test-d.ts @@ -0,0 +1,5 @@ +import {expectType} from '../../..'; +import one from './utils'; + +expectType(one('foo', 'bar')); +expectType(one(1, 2)); diff --git a/source/test/fixtures/typings-custom-dir/package.json b/source/test/fixtures/typings-custom-dir/package.json new file mode 100644 index 00000000..de6dc1db --- /dev/null +++ b/source/test/fixtures/typings-custom-dir/package.json @@ -0,0 +1,3 @@ +{ + "name": "foo" +} diff --git a/source/test/fixtures/typings-custom-dir/utils/index.d.ts b/source/test/fixtures/typings-custom-dir/utils/index.d.ts new file mode 100644 index 00000000..0616ebaa --- /dev/null +++ b/source/test/fixtures/typings-custom-dir/utils/index.d.ts @@ -0,0 +1,6 @@ +declare const one: { + (foo: string, bar: string): string; + (foo: number, bar: number): number; +}; + +export default one; diff --git a/source/test/test.ts b/source/test/test.ts index ebe99335..320aa507 100644 --- a/source/test/test.ts +++ b/source/test/test.ts @@ -220,3 +220,36 @@ test('strict types', async t => { verify(t, diagnostics, []); }); + +test('typings in custom directory', async t => { + const diagnostics = await tsd({ + cwd: path.join(__dirname, 'fixtures/typings-custom-dir'), + typingsFile: 'utils/index.d.ts' + }); + + verify(t, diagnostics, [ + [5, 19, 'error', 'Argument of type \'number\' is not assignable to parameter of type \'string\'.'] + ]); +}); + +test('specify test files manually', async t => { + const diagnostics = await tsd({ + cwd: path.join(__dirname, 'fixtures/specify-test-files'), + testFiles: [ + 'unknown.test.ts' + ] + }); + + verify(t, diagnostics, [ + [5, 19, 'error', 'Argument of type \'number\' is not assignable to parameter of type \'string\'.'] + ]); +}); + +test('fails if typings file is not found in the specified path', async t => { + const error = await t.throwsAsync(tsd({ + cwd: path.join(__dirname, 'fixtures/typings-custom-dir'), + typingsFile: 'unknown.d.ts' + })); + + t.is(error.message, 'The type definition `unknown.d.ts` does not exist. Create one and try again.'); +});