Skip to content

Commit

Permalink
feat(cli): ads a simple config:init helper + tests
Browse files Browse the repository at this point in the history
  • Loading branch information
huafu committed Sep 4, 2018
1 parent 48ab7bb commit 6700522
Show file tree
Hide file tree
Showing 6 changed files with 446 additions and 26 deletions.
2 changes: 1 addition & 1 deletion cli.js
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
#!/usr/bin/env node

require('./dist/cli')
require('./dist/cli').processArgv()
16 changes: 16 additions & 0 deletions docs/user/config.md
Original file line number Diff line number Diff line change
Expand Up @@ -89,3 +89,19 @@ All options have default values which should fit most of the projects.
- [**`diagnostics`**: Diagnostics related configuration.](config/diagnostics)
- [**`babelConfig`**: Babel(Jest) related configuration.](config/babelConfig)
- [**`stringifyContentPathRegex`**: Configure which file(s) will become a module returning its content.](config/stringifyContentPathRegex)

### Upgrading

You can use the `config:migrate` tool of TSJest CLI if you're coming from an older version to help you migrate your Jest configuration.

<div class="row"><div class="col-md-6" markdown="block">
If you're using `jest.config.json`:
```sh
node ./node_modules/.bin/ts-jest config:migrate jest.config.js
```
</div><div class="col-md-6" markdown="block">
If you're using `jest` config property of `package.json`:
```sh
node ./node_modules/.bin/ts-jest config:migrate package.json
```
</div></div>
284 changes: 284 additions & 0 deletions src/cli/cli.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,284 @@
import { testing } from 'bs-logger'
import * as _fs from 'fs'
import { normalize, resolve } from 'path'

import { mocked } from '../__helpers__/mocks'
import { rootLogger as _rootLogger } from '../util/logger'

import { processArgv } from '.'

// === helpers ================================================================
jest.mock('../util/logger')
jest.mock('fs')
const fs = mocked(_fs)
const rootLogger = _rootLogger as testing.LoggerMock

const mockWriteStream = () => {
return {
written: [] as string[],
write(text: string) {
this.written.push(text)
},
clear() {
this.written = []
},
}
}

const mockObject = <T, M>(obj: T, newProps: M): T & M & { mockRestore: () => T } => {
const backup: any = Object.create(null)

Object.keys(newProps).forEach(key => {
const desc = (backup[key] = Object.getOwnPropertyDescriptor(obj, key))
const newDesc: any = { ...desc }
if (newDesc.get) {
newDesc.get = () => (newProps as any)[key]
} else {
newDesc.value = (newProps as any)[key]
}
Object.defineProperty(obj, key, newDesc)
})
if ((obj as any).mockRestore) backup.mockRestore = Object.getOwnPropertyDescriptor(obj, 'mockRestore')
return Object.defineProperty(obj, 'mockRestore', {
value() {
Object.keys(backup).forEach(key => {
Object.defineProperty(obj, key, backup[key])
})
return obj
},
configurable: true,
})
}

let lastExitCode: number | undefined

const runCli = async (
...args: any[]
): Promise<{ stdout: string; stderr: string; exitCode: number | undefined; log: string }> => {
mockedProcess.stderr.clear()
mockedProcess.stdout.clear()
rootLogger.target.clear()
mockedProcess.argv.splice(2, mockedProcess.argv.length - 2, ...args)
lastExitCode = undefined
await processArgv()
return {
exitCode: lastExitCode,
stdout: mockedProcess.stdout.written.join('\n'),
stderr: mockedProcess.stderr.written.join('\n'),
log: rootLogger.target.lines.join('\n'),
}
}

let mockedProcess: any
const FAKE_CWD = normalize('/foo/bar')
const FAKE_PKG = normalize(`${FAKE_CWD}/package.json`)
fs.existsSync.mockImplementation(f => f === FAKE_PKG)
fs.readFileSync.mockImplementation(f => {
if (f === FAKE_PKG) return JSON.stringify({ name: 'mock', version: '0.0.0-mock.0' })
throw new Error('ENOENT')
})

// === test ===================================================================

beforeEach(() => {
// jest.resetModules()
lastExitCode = undefined
mockedProcess = mockObject(process, {
cwd: () => FAKE_CWD,
argv: ['node', resolve(__dirname, '..', '..', 'cli.js')],
stderr: mockWriteStream(),
stdout: mockWriteStream(),
exit: (exitCode = 0) => {
lastExitCode = exitCode
},
})
fs.writeFileSync.mockClear()
fs.existsSync.mockClear()
fs.readFileSync.mockClear()
rootLogger.target.clear()
})
afterEach(() => {
mockedProcess.mockRestore()
mockedProcess = undefined
})

describe('cli', async () => {
it('should output usage', async () => {
expect.assertions(2)
await expect(runCli()).resolves.toMatchInlineSnapshot(`
Object {
"exitCode": 0,
"log": "",
"stderr": "",
"stdout": "
Usage:
ts-jest command [options] [...args]
Commands:
config:init Creates initial Jest configuration
config:migrate Migrates a given Jest configuration
help [command] Show this help, or help about a command
Example:
ts-jest help config:migrate
",
}
`)
await expect(runCli('hello:motto')).resolves.toMatchInlineSnapshot(`
Object {
"exitCode": 0,
"log": "",
"stderr": "",
"stdout": "
Usage:
ts-jest command [options] [...args]
Commands:
config:init Creates initial Jest configuration
config:migrate Migrates a given Jest configuration
help [command] Show this help, or help about a command
Example:
ts-jest help config:migrate
",
}
`)
})
})

describe('config', async () => {
// briefly tested, see header comment in `config/init.ts`
describe('init', async () => {
const noOption = ['config:init']
const fullOptions = [
...noOption,
'--babel',
'--tsconfig',
'tsconfig.test.json',
'--jsdom',
'--no-jest-preset',
'--allow-js',
]
it('should create a jest.config.json (without options)', async () => {
expect.assertions(2)
const res = await runCli(...noOption)
expect(res).toEqual({
exitCode: 0,
log: '',
stderr: `
Jest configuration written to "${normalize('/foo/bar/jest.config.js')}".
`,
stdout: '',
})
expect(fs.writeFileSync.mock.calls).toEqual([
[
normalize('/foo/bar/jest.config.js'),
`module.exports = {
preset: 'ts-jest',
testEnvironment: 'node',
};`,
],
])
})
it('should create a jest.config.foo.json (with all options set)', async () => {
expect.assertions(2)
const res = await runCli(...fullOptions, 'jest.config.foo.js')
expect(res).toEqual({
exitCode: 0,
log: '',
stderr: `
Jest configuration written to "${normalize('/foo/bar/jest.config.foo.js')}".
`,
stdout: '',
})
expect(fs.writeFileSync.mock.calls).toEqual([
[
normalize('/foo/bar/jest.config.foo.js'),
`const tsJest = require('ts-jest').createJestPreset({ allowJs: true });
module.exports = {
...tsJest,
globals: {
'ts-jest': {
tsconfig: 'tsconfig.test.json',
babelConfig: true,
},
},
};`,
],
])
})
it('should update package.json (without options)', async () => {
expect.assertions(2)
const res = await runCli(...noOption, 'package.json')
expect(res).toEqual({
exitCode: 0,
log: '',
stderr: `
Jest configuration written to "${normalize('/foo/bar/package.json')}".
`,
stdout: '',
})
expect(fs.writeFileSync.mock.calls).toEqual([
[
normalize('/foo/bar/package.json'),
`{
"name": "mock",
"version": "0.0.0-mock.0",
"jest": {
"preset": "ts-jest",
"testEnvironment": "node"
}
}`,
],
])
})
it('should update package.json (with all options set)', async () => {
expect.assertions(2)
const res = await runCli(...fullOptions, 'package.json')
expect(res).toEqual({
exitCode: 0,
log: `[level:20] creating jest presets handling JavaScript files
`,
stderr: `
Jest configuration written to "${normalize('/foo/bar/package.json')}".
`,
stdout: '',
})
expect(fs.writeFileSync.mock.calls).toEqual([
[
normalize('/foo/bar/package.json'),
`{
"name": "mock",
"version": "0.0.0-mock.0",
"jest": {
"transform": {
"^.+\\\\.[tj]sx?$": "ts-jest"
},
"testMatch": [
"**/__tests__/**/*.js?(x)",
"**/?(*.)+(spec|test).js?(x)",
"**/__tests__/**/*.ts?(x)",
"**/?(*.)+(spec|test).ts?(x)"
],
"moduleFileExtensions": [
"js",
"json",
"jsx",
"node",
"ts",
"tsx"
],
"globals": {
"ts-jest": {
"tsconfig": "tsconfig.test.json",
"babelConfig": true
}
}
}
}`,
],
])
})
})
})
Loading

0 comments on commit 6700522

Please sign in to comment.