Skip to content

Commit

Permalink
Browse files Browse the repository at this point in the history
feat(config): show a warning message when TypeScript target version…
… doesn't match with recommended NodeJs version (#1678)
  • Loading branch information
ahnpnl committed May 25, 2020
1 parent 9c6f98e commit 085bdf5
Show file tree
Hide file tree
Showing 11 changed files with 170 additions and 53 deletions.
15 changes: 11 additions & 4 deletions e2e/__tests__/__snapshots__/logger.test.ts.snap
@@ -1,6 +1,6 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP

exports[`TS_JEST_LOG should pass and create log file when using template "default" 1`] = `
exports[`ts-jest logging TS_JEST_LOG should pass and create log file when using template "default" 1`] = `
Array [
"[level:20] creating jest presets not handling JavaScript files",
"[level:20] creating Importer singleton",
Expand Down Expand Up @@ -37,7 +37,7 @@ Array [
]
`;
exports[`TS_JEST_LOG should pass and create log file when using template "with-babel-7" 1`] = `
exports[`ts-jest logging TS_JEST_LOG should pass and create log file when using template "with-babel-7" 1`] = `
Array [
"[level:20] creating jest presets not handling JavaScript files",
"[level:20] creating Importer singleton",
Expand Down Expand Up @@ -80,7 +80,7 @@ Array [
]
`;
exports[`TS_JEST_LOG should pass and create log file when using template "with-babel-7-string-config" 1`] = `
exports[`ts-jest logging TS_JEST_LOG should pass and create log file when using template "with-babel-7-string-config" 1`] = `
Array [
"[level:20] creating jest presets not handling JavaScript files",
"[level:20] creating Importer singleton",
Expand Down Expand Up @@ -124,7 +124,7 @@ Array [
]
`;
exports[`With unsupported version test should pass using template "with-unsupported-version" 1`] = `
exports[`ts-jest logging with unsupported version test should pass using template "with-unsupported-version" 1`] = `
√ jest
↳ exit code: 0
===[ STDOUT ]===================================================================
Expand All @@ -142,3 +142,10 @@ exports[`With unsupported version test should pass using template "with-unsuppor
Ran all test suites.
================================================================================
`;
exports[`ts-jest logging typescript target is higher than es2019 for NodeJs 12 should pass using template "default" 1`] = `
Array [
"[level:40] There is a mismatch between your NodeJs version v12.16.3 and your TypeScript target es2020. This might lead to some unexpected errors when running tests with \`ts-jest\`. To fix this, you can check https://github.com/microsoft/TypeScript/wiki/Node-Target-Mapping",
"[level:40] message TS151001: If you have issues related to imports, you should consider setting \`esModuleInterop\` to \`true\` in your TypeScript configuration file (usually \`tsconfig.json\`). See https://blogs.msdn.microsoft.com/typescript/2018/01/31/announcing-typescript-2-7/#easier-ecmascript-module-interoperability for more information.",
]
`;
81 changes: 57 additions & 24 deletions e2e/__tests__/logger.test.ts
Expand Up @@ -4,35 +4,68 @@ import { existsSync } from 'fs'
import { PackageSets, allValidPackageSets } from '../__helpers__/templates'
import { configureTestCase } from '../__helpers__/test-case'

describe('With unsupported version test', () => {
const testCase = configureTestCase('simple')
describe('ts-jest logging', () => {
describe('with unsupported version test', () => {
const testCase = configureTestCase('simple')

testCase.runWithTemplates([PackageSets.unsupportedVersion], 0, (runTest, { testLabel }) => {
it(testLabel, () => {
const result = runTest()
expect(result.status).toBe(0)
expect(result).toMatchSnapshot()
testCase.runWithTemplates([PackageSets.unsupportedVersion], 0, (runTest, { testLabel }) => {
it(testLabel, () => {
const result = runTest()
expect(result.status).toBe(0)
expect(result).toMatchSnapshot()
})
})
})
})

describe('TS_JEST_LOG', () => {
const testCase = configureTestCase('simple', {
env: { TS_JEST_LOG: 'ts-jest.log' },
noCache: true,
})
describe('TS_JEST_LOG', () => {
const testCase = configureTestCase('simple', {
env: { TS_JEST_LOG: 'ts-jest.log' },
noCache: true,
})

testCase.runWithTemplates(allValidPackageSets, 0, (runTest, { templateName }) => {
it(`should pass and create log file when using template "${templateName}"`, () => {
const result = runTest()
expect(result.status).toBe(0)
expect(existsSync(result.logFilePath)).toBe(true)
const filteredEntries = result.logFileEntries
// keep only debug and above
.filter(m => (m.context[LogContexts.logLevel] || 0) >= LogLevels.debug)
// simplify entires
.map(e => result.normalize(`[level:${e.context[LogContexts.logLevel]}] ${e.message}`))
expect(filteredEntries).toMatchSnapshot()
testCase.runWithTemplates(allValidPackageSets, 0, (runTest, { templateName }) => {
it(`should pass and create log file when using template "${templateName}"`, () => {
const result = runTest()
expect(result.status).toBe(0)
expect(existsSync(result.logFilePath)).toBe(true)
const filteredEntries = result.logFileEntries
// keep only debug and above
.filter(m => (m.context[LogContexts.logLevel] || 0) >= LogLevels.debug)
// simplify entires
.map(e => result.normalize(`[level:${e.context[LogContexts.logLevel]}] ${e.message}`))
expect(filteredEntries).toMatchSnapshot()
})
})
})

/**
* Since we only run e2e for node 12 so we need this if here. We follow latest LTS Node version so once latest LTS version
* changes, we also need to change this test.
*/
if (process.version.startsWith('v12')) {
describe('typescript target is higher than es2019 for NodeJs 12', () => {
const testCase = configureTestCase('simple', {
env: { TS_JEST_LOG: 'ts-jest.log' },
noCache: true,
tsJestConfig: {
tsConfig: {
target: 'es2020'
}
}
})

testCase.runWithTemplates([PackageSets.default], 0, (runTest, { testLabel }) => {
it(testLabel, () => {
const result = runTest()
expect(result.status).toBe(0)
const filteredEntries = result.logFileEntries
// keep only debug and above
.filter(m => (m.context[LogContexts.logLevel] || 0) === LogLevels.warn)
// simplify entires
.map(e => result.normalize(`[level:${e.context[LogContexts.logLevel]}] ${e.message}`))
expect(filteredEntries).toMatchSnapshot()
})
})
})
}
})
9 changes: 4 additions & 5 deletions e2e/jest.config.js
@@ -1,11 +1,10 @@
const jestBaseConfig = require('../jest-base')

/** @type {import('@jest/types').Config.InitialOptions} */
module.exports = {
...jestBaseConfig,
rootDir: '..',
transform: {
'\\.ts$': '<rootDir>/dist/index.js',
},
testMatch: ['<rootDir>/e2e/__tests__/**/*.test.ts'],
moduleFileExtensions: ['ts', 'tsx', 'js', 'jsx', 'json'],
testEnvironment: 'node',
snapshotSerializers: [
'<rootDir>/e2e/__serializers__/run-result.ts',
'<rootDir>/e2e/__serializers__/processed-source.ts',
Expand Down
12 changes: 12 additions & 0 deletions jest-base.js
@@ -0,0 +1,12 @@
/** @type {import('@jest/types').Config.InitialOptions} */
module.exports = {
globals: {
'ts-jest': {
tsConfig: 'tsconfig.spec.json',
},
},
transform: {
'\\.ts$': '<rootDir>/dist/index.js',
},
testEnvironment: 'node',
}
9 changes: 4 additions & 5 deletions jest.config.js
@@ -1,9 +1,10 @@
const baseConfig = require('./jest-base')

/** @type {import('@jest/types').Config.InitialOptions} */
module.exports = {
...baseConfig,
rootDir: '.',
setupFilesAfterEnv: ['<rootDir>/src/__helpers__/setup.ts'],
transform: {
'\\.ts$': '<rootDir>/dist/index.js',
},
testMatch: ['<rootDir>/src/**/*.spec.ts'],
testPathIgnorePatterns: ['<rootDir>/src/__mocks__/*'],
collectCoverageFrom: [
Expand All @@ -14,8 +15,6 @@ module.exports = {
'!<rootDir>/src/**/__*__/*',
'!<rootDir>/src/util/testing.ts',
],
moduleFileExtensions: ['ts', 'tsx', 'js', 'jsx', 'json'],
testEnvironment: 'node',
snapshotSerializers: ['<rootDir>/src/__serializers__/processed-source.ts'],
cacheDirectory: '<rootDir>/.cache/unit',
}
55 changes: 54 additions & 1 deletion src/config/config-set.spec.ts
@@ -1,7 +1,7 @@
/* eslint-disable jest/no-mocks-import */
import { Transformer } from '@jest/transform'
import { Config } from '@jest/types'
import { testing } from 'bs-logger'
import { LogLevels, testing } from 'bs-logger'
import { readFileSync } from 'fs'
import json5 = require('json5')
import { resolve } from 'path'
Expand Down Expand Up @@ -792,6 +792,59 @@ describe('readTsConfig', () => {
})
})
})

describe('mismatch nodejs version and typescript target', () => {
const logTarget = logTargetMock()

beforeEach(() => {
logTarget.clear()
cs = createConfigSet({ jestConfig: { rootDir: '/root', cwd: '/cwd' } as any })
findConfig.mockImplementation((p) => `${p}/tsconfig.json`)
})

afterEach(() => {
findConfig.mockClear()
})

function mismatchTestCaseContent(tsTarget: string, scriptTarget: ts.ScriptTarget) {
parseConfig.mockImplementation((conf: any) => ({
options: {
...conf,
target: scriptTarget,
},
fileNames: [],
errors: [],
}))
readConfig.mockImplementation((p) => ({ config: { path: p, compilerOptions: { target: tsTarget } } }))

cs.readTsConfig()

// expect.toEqual gives weird result here so toContain is workaround for it.
expect(logTarget.filteredLines(LogLevels.warn, Infinity)[0]).toContain(
'[level:40] There is a mismatch between your ' +
`NodeJs version ${process.version} and your TypeScript target ${tsTarget}. This might lead to some unexpected errors ` +
'when running tests with `ts-jest`. To fix this, you can check https://github.com/microsoft/TypeScript/wiki/Node-Target-Mapping',
)

parseConfig.mockClear()
readConfig.mockClear()
}

/**
* It seems like not possible to mock process.version so the condition here is needed
*/
if (process.version.startsWith('v10')) {
// eslint-disable-next-line jest/expect-expect
it('should show warning message when nodejs version is 10 and typescript target is higher than es2018', () => {
mismatchTestCaseContent('es2019', ts.ScriptTarget.ES2019)
})
} else {
// eslint-disable-next-line jest/expect-expect
it('should show warning message when nodejs version is 12 and typescript target is higher than es2019', () => {
mismatchTestCaseContent('es2020', ts.ScriptTarget.ES2020)
})
}
})
}) // readTsConfig

describe('versions', () => {
Expand Down
21 changes: 19 additions & 2 deletions src/config/config-set.ts
Expand Up @@ -20,6 +20,7 @@ import {
DiagnosticCategory,
FormatDiagnosticsHost,
ParsedCommandLine,
ScriptTarget,
SourceFile,
} from 'typescript'

Expand All @@ -31,11 +32,11 @@ import {
AstTransformerDesc,
BabelConfig,
BabelJestTransformer,
TTypeScript,
TsCompiler,
TsJestConfig,
TsJestGlobalOptions,
TsJestHooksMap,
TTypeScript,
} from '../types'
import { backportJestConfig } from '../util/backports'
import { getPackageVersion } from '../util/get-package-version'
Expand Down Expand Up @@ -723,7 +724,7 @@ export class ConfigSet {
resolvedConfigFile?: string | null,
noProject?: boolean | null,
): ParsedCommandLine {
let config = { compilerOptions: {} }
let config = { compilerOptions: Object.create(null) }
let basePath = normalizeSlashes(this.rootDir)
let configFileName: string | undefined
const ts = this.compilerModule
Expand Down Expand Up @@ -800,6 +801,22 @@ export class ConfigSet {
finalOptions[key] = val
}
}
/**
* See https://github.com/microsoft/TypeScript/wiki/Node-Target-Mapping
* Every time this page is updated, we also need to update here. Here we only show warning message for Node LTS versions
*/
const nodeJsVer = process.version
const compilationTarget = result.options.target!
if (
(nodeJsVer.startsWith('v10') && compilationTarget > ScriptTarget.ES2018) ||
(nodeJsVer.startsWith('v12') && compilationTarget > ScriptTarget.ES2019)
) {
const message = interpolate(Errors.MismatchNodeTargetMapping, {
nodeJsVer: process.version,
compilationTarget: config.compilerOptions.target,
})
logger.warn(message)
}

return result
}
Expand Down
1 change: 1 addition & 0 deletions src/util/messages.ts
Expand Up @@ -18,6 +18,7 @@ export const enum Errors {
GotUnknownFileTypeWithBabel = 'Got a unknown file type to compile (file: {{path}}). To fix this, in your Jest config change the `transform` key which value is `ts-jest` so that it does not match this kind of files anymore. If you still want Babel to process it, add another entry to the `transform` option with value `babel-jest` which key matches this type of files.',
ConfigNoModuleInterop = 'If you have issues related to imports, you should consider setting `esModuleInterop` to `true` in your TypeScript configuration file (usually `tsconfig.json`). See https://blogs.msdn.microsoft.com/typescript/2018/01/31/announcing-typescript-2-7/#easier-ecmascript-module-interoperability for more information.',
UnableToFindProjectRoot = 'Unable to find the root of the project where ts-jest has been installed.',
MismatchNodeTargetMapping = 'There is a mismatch between your NodeJs version {{nodeJsVer}} and your TypeScript target {{compilationTarget}}. This might lead to some unexpected errors when running tests with `ts-jest`. To fix this, you can check https://github.com/microsoft/TypeScript/wiki/Node-Target-Mapping',
}

/**
Expand Down
4 changes: 3 additions & 1 deletion src/util/version-checkers.ts
Expand Up @@ -9,7 +9,7 @@ const logger = rootLogger.child({ namespace: 'versions' })
/**
* @internal
*/
export const enum ExpectedVersions {
const enum ExpectedVersions {
Jest = '>=26 <27',
TypeScript = '>=3.8 <4',
BabelJest = '>=26 <27',
Expand Down Expand Up @@ -49,6 +49,7 @@ function checkVersion(
): boolean | never {
const version = getPackageVersion(name)
const success = !!version && satisfies(version, expectedRange)

logger.debug(
{
actualVersion: version,
Expand All @@ -58,6 +59,7 @@ function checkVersion(
name,
success ? 'OK' : 'NOT OK',
)

if (!action || success) return success

const message = interpolate(version ? Errors.UntestedDependencyVersion : Errors.MissingDependency, {
Expand Down
12 changes: 1 addition & 11 deletions tsconfig.json
Expand Up @@ -30,15 +30,5 @@
"node",
"react"
]
},
"include": [
"e2e/__helpers__",
"e2e/__serializers__",
"e2e/__tests__",
"scripts/",
"src/",
"utils/",
"presets",
"./*.js"
]
}
}
4 changes: 4 additions & 0 deletions tsconfig.spec.json
@@ -0,0 +1,4 @@
{
"extends": "./tsconfig.json",
"include": []
}

0 comments on commit 085bdf5

Please sign in to comment.