Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(config): show warning message for nodejs ts target version mismatch #1678

Merged
merged 1 commit into from May 25, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
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": []
}