From eeaefd653fbfc9eebb86cf914829cc19a143cbae Mon Sep 17 00:00:00 2001 From: Hiroki Osame Date: Tue, 21 May 2024 11:40:41 +0900 Subject: [PATCH] test: separate tsconfig tests --- tests/fixtures.ts | 154 ++++++++--------------------------- tests/index.ts | 1 + tests/specs/smoke.ts | 105 ++++++++---------------- tests/specs/tsconfig.ts | 144 ++++++++++++++++++++++++++++++++ tests/utils/package-types.ts | 4 + 5 files changed, 215 insertions(+), 193 deletions(-) create mode 100644 tests/specs/tsconfig.ts create mode 100644 tests/utils/package-types.ts diff --git a/tests/fixtures.ts b/tests/fixtures.ts index 9a111ca1..2c13658c 100644 --- a/tests/fixtures.ts +++ b/tests/fixtures.ts @@ -6,7 +6,8 @@ const React = { createElement: (...args) => Array.from(args), }; `; -const jsxCheck = '<>
JSX
'; + +export const jsxCheck = '<>
JSX
'; const preserveName = ` assert( @@ -83,7 +84,39 @@ const sourcemap = { }, }; +export const expectErrors = { + 'expect-errors.js': ` + export const expectErrors = async (...assertions) => { + let errors = await Promise.all( + assertions.map(async ([fn, expectedError]) => { + let thrown; + try { + await fn(); + } catch (error) { + thrown = error; + } + + if (!thrown) { + return new Error('No error thrown'); + } else if (!thrown.message.includes(expectedError)) { + return new Error(\`Message \${JSON.stringify(expectedError)} not found in \${JSON.stringify(thrown.message)}\n\${thrown.stack}\`); + } + }), + ); + + errors = errors.filter(Boolean); + + if (errors.length > 0) { + console.error(errors); + process.exitCode = 1; + } + }; + `, +}; + export const files = { + ...expectErrors, + 'js/index.js': ` import assert from 'assert'; ${syntaxLowering} @@ -188,44 +221,8 @@ export const files = { ${sourcemap.test('cts')} `, - 'expect-errors.js': ` - export const expectErrors = async (...assertions) => { - let errors = await Promise.all( - assertions.map(async ([fn, expectedError]) => { - let thrown; - try { - await fn(); - } catch (error) { - thrown = error; - } - - if (!thrown) { - return new Error('No error thrown'); - } else if (!thrown.message.includes(expectedError)) { - return new Error(\`Message \${JSON.stringify(expectedError)} not found in \${JSON.stringify(thrown.message)}\n\${thrown.stack}\`); - } - }), - ); - - errors = errors.filter(Boolean); - - if (errors.length > 0) { - console.error(errors); - process.exitCode = 1; - } - }; - `, - 'file.txt': 'hello', - 'import-typescript-parent.js': sourcemap.tag` - import './import-typescript-child.js'; - `, - - 'import-typescript-child.ts': sourcemap.tag` - console.log('imported'); - `, - 'broken-syntax.ts': 'if', node_modules: { @@ -244,87 +241,4 @@ export const files = { 'empty-export/index.js': 'export {}', }, }, - - tsconfig: { - 'file.ts': '', - - 'jsx.jsx': ` - // tsconfig not applied to jsx because allowJs is not set - import { expectErrors } from '../expect-errors'; - expectErrors( - [() => ${jsxCheck}, 'React is not defined'], - - // These should throw unless allowJs is set - // [() => import ('prefix/file'), "Cannot find package 'prefix'"], - // [() => import ('paths-exact-match'), "Cannot find package 'paths-exact-match'"], - // [() => import ('file'), "Cannot find package 'file'"], - ); - `, - - 'node_modules/tsconfig-should-not-apply': { - 'package.json': JSON.stringify({ - exports: { - import: './index.mjs', - default: './index.cjs', - }, - }), - 'index.mjs': ` - import { expectErrors } from '../../../expect-errors'; - expectErrors( - [() => import ('prefix/file'), "Cannot find package 'prefix'"], - [() => import ('paths-exact-match'), "Cannot find package 'paths-exact-match'"], - [() => import ('file'), "Cannot find package 'file'"], - ); - `, - 'index.cjs': ` - const { expectErrors } = require('../../../expect-errors'); - expectErrors( - [() => require('prefix/file'), "Cannot find module"], - [() => require('paths-exact-match'), "Cannot find module"], - [() => require('file'), "Cannot find module"], - ); - `, - }, - - 'index.tsx': ` - ${jsxCheck}; - - import './jsx'; - - // Resolves relative to baseUrl - import 'file'; - - // Resolves paths - exact match - import 'paths-exact-match'; - - // Resolves paths - prefix match - import 'prefix/file'; - - // Resolves paths - suffix match - import 'file/suffix'; - - // tsconfig should not apply to dependency - import "tsconfig-should-not-apply"; - `, - - 'tsconfig.json': JSON.stringify({ - compilerOptions: { - jsxFactory: 'Array', - jsxFragmentFactory: 'null', - baseUrl: '.', - paths: { - 'paths-exact-match': ['file'], - 'prefix/*': ['*'], - '*/suffix': ['*'], - }, - }, - }), - - 'tsconfig-allowJs.json': JSON.stringify({ - extends: './tsconfig.json', - compilerOptions: { - allowJs: true, - }, - }), - }, }; diff --git a/tests/index.ts b/tests/index.ts index 588a1b36..0d4177c2 100644 --- a/tests/index.ts +++ b/tests/index.ts @@ -15,6 +15,7 @@ import { nodeVersions } from './utils/node-versions'; await runTestSuite(import('./specs/watch'), node); await runTestSuite(import('./specs/loaders'), node); await runTestSuite(import('./specs/smoke'), node); + await runTestSuite(import('./specs/tsconfig'), node); }); } }); diff --git a/tests/specs/smoke.ts b/tests/specs/smoke.ts index 645ba158..62082266 100644 --- a/tests/specs/smoke.ts +++ b/tests/specs/smoke.ts @@ -7,23 +7,19 @@ import type { NodeApis } from '../utils/tsx.js'; import { hasCoverageSourcesContent } from '../utils/coverage-sources-content.js'; import { isWindows } from '../utils/is-windows.js'; import { files } from '../fixtures.js'; +import { packageTypes } from '../utils/package-types.js'; const wasmPath = path.resolve('tests/fixtures/test.wasm'); const wasmPathUrl = pathToFileURL(wasmPath).toString(); -const packageTypes = [ - 'module', - 'commonjs', -] as const; - export default testSuite(async ({ describe }, { tsx }: NodeApis) => { describe('Smoke', ({ describe }) => { for (const packageType of packageTypes) { const isCommonJs = packageType === 'commonjs'; - describe(packageType, ({ test, describe }) => { - test('from .js', async ({ onTestFinish, onTestFail }) => { - const fixture = await createFixture({ + describe(packageType, ({ test }) => { + test('from .js', async ({ onTestFail }) => { + await using fixture = await createFixture({ 'package.json': JSON.stringify({ type: packageType }), 'import-from-js.js': outdent` import assert from 'assert'; @@ -133,7 +129,6 @@ export default testSuite(async ({ describe }, { tsx }: NodeApis) => { `, ...files, }); - onTestFinish(async () => await fixture.rm()); const p = await tsx(['import-from-js.js'], fixture.path); onTestFail((error) => { @@ -158,8 +153,8 @@ export default testSuite(async ({ describe }, { tsx }: NodeApis) => { expect(p.stderr).toBe(''); }); - describe('from .ts', async ({ test, onFinish }) => { - const fixture = await createFixture({ + test('from .ts', async ({ onTestFail }) => { + await using fixture = await createFixture({ 'package.json': JSON.stringify({ type: packageType }), 'import-from-ts.ts': ({ fixturePath }) => outdent` @@ -333,73 +328,37 @@ export default testSuite(async ({ describe }, { tsx }: NodeApis) => { `, ...files, }); - onFinish(async () => await fixture.rm()); - - test('import all', async ({ onTestFail }) => { - const p = await tsx(['import-from-ts.ts'], { - cwd: fixture.path, - env: { - NODE_V8_COVERAGE: 'coverage', - }, - }); - onTestFail((error) => { - console.error(error); - console.log(p); - }); - expect(p.failed).toBe(false); - expect(p.stdout).toMatch(`"import.meta.url":"${pathToFileURL(fixture.getPath('import-from-ts.ts'))}"`); - expect(p.stdout).toMatch(`"js":{"cjsContext":${isCommonJs},"default":1,"named":2}`); - expect(p.stdout).toMatch('"json":{"default":{"loaded":"json"},"loaded":"json"}'); - expect(p.stdout).toMatch('"cjs":{"default":{"named":"named"},"named":"named"}'); - expect(p.stdout).toMatch(`"jsx":{"cjsContext":${isCommonJs},"jsx":[null,null,["div",null,"JSX"]]}`); - expect(p.stdout).toMatch('"pkgModule":{"default":1,"named":2}'); - if (isCommonJs) { - expect(p.stdout).toMatch('"pkgCommonjs":{"default":1,"named":2}'); - } else { - expect(p.stdout).toMatch('"pkgCommonjs":{"default":{"default":1,"named":2}}'); - } - // By "require()"ing an ESM file, it forces it to be compiled in a CJS context - expect(p.stdout).toMatch(`"mjs":{"mjsHasCjsContext":${isCommonJs}}`); - expect(p.stderr).toBe(''); - - const coverageDirectory = fixture.getPath('coverage'); - const coverageSourceMapCache = await hasCoverageSourcesContent(coverageDirectory); - expect(coverageSourceMapCache).toBe(true); + const p = await tsx(['import-from-ts.ts'], { + cwd: fixture.path, + env: { + NODE_V8_COVERAGE: 'coverage', + }, }); - - test('tsconfig', async ({ onTestFail }) => { - const pTsconfig = await tsx(['index.tsx'], fixture.getPath('tsconfig')); - onTestFail((error) => { - console.error(error); - console.log(pTsconfig); - }); - expect(pTsconfig.failed).toBe(false); - expect(pTsconfig.stderr).toBe(''); - expect(pTsconfig.stdout).toBe(''); + onTestFail((error) => { + console.error(error); + console.log(p); }); + expect(p.failed).toBe(false); + expect(p.stdout).toMatch(`"import.meta.url":"${pathToFileURL(fixture.getPath('import-from-ts.ts'))}"`); + expect(p.stdout).toMatch(`"js":{"cjsContext":${isCommonJs},"default":1,"named":2}`); + expect(p.stdout).toMatch('"json":{"default":{"loaded":"json"},"loaded":"json"}'); + expect(p.stdout).toMatch('"cjs":{"default":{"named":"named"},"named":"named"}'); + expect(p.stdout).toMatch(`"jsx":{"cjsContext":${isCommonJs},"jsx":[null,null,["div",null,"JSX"]]}`); + expect(p.stdout).toMatch('"pkgModule":{"default":1,"named":2}'); + if (isCommonJs) { + expect(p.stdout).toMatch('"pkgCommonjs":{"default":1,"named":2}'); + } else { + expect(p.stdout).toMatch('"pkgCommonjs":{"default":{"default":1,"named":2}}'); + } - test('custom tsconfig', async ({ onTestFail }) => { - const pTsconfigAllowJs = await tsx(['--tsconfig', 'tsconfig-allowJs.json', 'jsx.jsx'], fixture.getPath('tsconfig')); - onTestFail((error) => { - console.error(error); - console.log(pTsconfigAllowJs); - }); - expect(pTsconfigAllowJs.failed).toBe(true); - expect(pTsconfigAllowJs.stderr).toMatch('Error: No error thrown'); - expect(pTsconfigAllowJs.stdout).toBe(''); - }); + // By "require()"ing an ESM file, it forces it to be compiled in a CJS context + expect(p.stdout).toMatch(`"mjs":{"mjsHasCjsContext":${isCommonJs}}`); + expect(p.stderr).toBe(''); - test('allowJs in tsconfig.json', async ({ onTestFail }) => { - const pTsconfigAllowJs = await tsx(['--tsconfig', 'tsconfig/tsconfig-allowJs.json', 'import-typescript-parent.js'], fixture.path); - onTestFail((error) => { - console.error(error); - console.log(pTsconfigAllowJs); - }); - expect(pTsconfigAllowJs.failed).toBe(false); - expect(pTsconfigAllowJs.stderr).toBe(''); - expect(pTsconfigAllowJs.stdout).toBe('imported'); - }); + const coverageDirectory = fixture.getPath('coverage'); + const coverageSourceMapCache = await hasCoverageSourcesContent(coverageDirectory); + expect(coverageSourceMapCache).toBe(true); }); }); } diff --git a/tests/specs/tsconfig.ts b/tests/specs/tsconfig.ts new file mode 100644 index 00000000..b72f3ad5 --- /dev/null +++ b/tests/specs/tsconfig.ts @@ -0,0 +1,144 @@ +import { testSuite, expect } from 'manten'; +import { createFixture } from 'fs-fixture'; +import type { NodeApis } from '../utils/tsx.js'; +import { expectErrors, jsxCheck } from '../fixtures.js'; +import { packageTypes } from '../utils/package-types.js'; + +export default testSuite(async ({ describe }, { tsx }: NodeApis) => { + describe('tsconfig', ({ describe }) => { + for (const packageType of packageTypes) { + describe(packageType, async ({ test, onFinish }) => { + const fixture = await createFixture({ + ...expectErrors, + + 'package.json': JSON.stringify({ type: packageType }), + + 'import-typescript-parent.js': ` + import './import-typescript-child.js'; + `, + + 'import-typescript-child.ts': ` + console.log('imported'); + `, + + tsconfig: { + 'jsx.jsx': ` + // tsconfig not applied to jsx because allowJs is not set + import { expectErrors } from '../expect-errors'; + expectErrors( + [() => ${jsxCheck}, 'React is not defined'], + + // These should throw unless allowJs is set + // [() => import ('prefix/file'), "Cannot find package 'prefix'"], + // [() => import ('paths-exact-match'), "Cannot find package 'paths-exact-match'"], + // [() => import ('file'), "Cannot find package 'file'"], + ); + `, + + 'index.tsx': ` + ${jsxCheck}; + + import './jsx'; + + // Resolves relative to baseUrl + import 'file'; + + // Resolves paths - exact match + import 'paths-exact-match'; + + // Resolves paths - prefix match + import 'prefix/file'; + + // Resolves paths - suffix match + import 'file/suffix'; + + // tsconfig should not apply to dependency + import "tsconfig-should-not-apply"; + `, + + 'file.ts': '', + 'tsconfig-allowJs.json': JSON.stringify({ + extends: './tsconfig.json', + compilerOptions: { + allowJs: true, + }, + }), + + 'tsconfig.json': JSON.stringify({ + compilerOptions: { + jsxFactory: 'Array', + jsxFragmentFactory: 'null', + baseUrl: '.', + paths: { + 'paths-exact-match': ['file'], + 'prefix/*': ['*'], + '*/suffix': ['*'], + }, + }, + }), + + 'node_modules/tsconfig-should-not-apply': { + 'package.json': JSON.stringify({ + exports: { + import: './index.mjs', + default: './index.cjs', + }, + }), + 'index.mjs': ` + import { expectErrors } from '../../../expect-errors'; + expectErrors( + [() => import ('prefix/file'), "Cannot find package 'prefix'"], + [() => import ('paths-exact-match'), "Cannot find package 'paths-exact-match'"], + [() => import ('file'), "Cannot find package 'file'"], + ); + `, + 'index.cjs': ` + const { expectErrors } = require('../../../expect-errors'); + expectErrors( + [() => require('prefix/file'), "Cannot find module"], + [() => require('paths-exact-match'), "Cannot find module"], + [() => require('file'), "Cannot find module"], + ); + `, + }, + + }, + }); + onFinish(async () => await fixture.rm()); + + test('tsconfig', async ({ onTestFail }) => { + const pTsconfig = await tsx(['index.tsx'], fixture.getPath('tsconfig')); + onTestFail((error) => { + console.error(error); + console.log(pTsconfig); + }); + expect(pTsconfig.failed).toBe(false); + expect(pTsconfig.stderr).toBe(''); + expect(pTsconfig.stdout).toBe(''); + }); + + test('custom tsconfig', async ({ onTestFail }) => { + const pTsconfigAllowJs = await tsx(['--tsconfig', 'tsconfig-allowJs.json', 'jsx.jsx'], fixture.getPath('tsconfig')); + onTestFail((error) => { + console.error(error); + console.log(pTsconfigAllowJs); + }); + expect(pTsconfigAllowJs.failed).toBe(true); + expect(pTsconfigAllowJs.stderr).toMatch('Error: No error thrown'); + expect(pTsconfigAllowJs.stdout).toBe(''); + }); + + test('allowJs in tsconfig.json', async ({ onTestFail }) => { + const pTsconfigAllowJs = await tsx(['--tsconfig', 'tsconfig/tsconfig-allowJs.json', 'import-typescript-parent.js'], fixture.path); + onTestFail((error) => { + console.error(error); + console.log(pTsconfigAllowJs); + }); + expect(pTsconfigAllowJs.failed).toBe(false); + expect(pTsconfigAllowJs.stderr).toBe(''); + expect(pTsconfigAllowJs.stdout).toBe('imported'); + }); + }); + } + }); +}); diff --git a/tests/utils/package-types.ts b/tests/utils/package-types.ts new file mode 100644 index 00000000..6585dd73 --- /dev/null +++ b/tests/utils/package-types.ts @@ -0,0 +1,4 @@ +export const packageTypes = [ + 'module', + 'commonjs', +] as const;