forked from umijs/father
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat: support generate jest (umijs/father-next#66)
* feat: support generate jest * test: add generate jest test * refactor: adjust jest.config * chore: move to tests * feat: add dumi cov and remove umi cov
- Loading branch information
1 parent
a433e54
commit dc9bdc2
Showing
5 changed files
with
254 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,100 @@ | ||
import { GeneratorType } from '@umijs/core'; | ||
import { logger } from '@umijs/utils'; | ||
import { existsSync, writeFileSync } from 'fs'; | ||
import { join } from 'path'; | ||
import { IApi } from '../../types'; | ||
import { GeneratorHelper, promptsExitWhenCancel } from './utils'; | ||
|
||
export default (api: IApi) => { | ||
api.describe({ | ||
key: 'generator:jest', | ||
}); | ||
|
||
api.registerGenerator({ | ||
key: 'jest', | ||
name: 'Enable Jest', | ||
description: 'Setup Jest Configuration', | ||
type: GeneratorType.enable, | ||
checkEnable: () => { | ||
return ( | ||
!existsSync(join(api.paths.cwd, 'jest.config.ts')) && | ||
!existsSync(join(api.paths.cwd, 'jest.config.js')) | ||
); | ||
}, | ||
disabledDescription: | ||
'Jest has already enabled. You can remove jest.config.{ts,js}, then run this again to re-setup.', | ||
fn: async () => { | ||
const h = new GeneratorHelper(api); | ||
|
||
const res = await promptsExitWhenCancel({ | ||
type: 'confirm', | ||
name: 'useRTL', | ||
message: 'Will you use @testing-library/react for UI testing?', | ||
initial: true, | ||
}); | ||
|
||
const basicDeps = { | ||
jest: '^27', | ||
'@types/jest': '^27', | ||
// we use `jest.config.ts` so jest needs ts and ts-node | ||
typescript: '^4', | ||
'ts-node': '^10', | ||
'@umijs/test': '^4', | ||
}; | ||
|
||
const deps: Record<string, string> = res.useRTL | ||
? { | ||
...basicDeps, | ||
'@testing-library/react': '^13', | ||
'@testing-library/jest-dom': '^5.16.4', | ||
'@types/testing-library__jest-dom': '^5.14.5', | ||
} | ||
: basicDeps; | ||
|
||
h.addDevDeps(deps); | ||
h.addScript('test', 'jest'); | ||
|
||
if (res.useRTL) { | ||
writeFileSync( | ||
join(api.cwd, 'jest-setup.ts'), | ||
`import '@testing-library/jest-dom'; | ||
`.trimStart(), | ||
); | ||
logger.info('Write jest-setup.ts'); | ||
} | ||
|
||
const collectCoverageFrom = ['src/**/*.{ts,js,tsx,jsx}']; | ||
const hasDumi = Object.keys(api.pkg.devDependencies || {}).includes( | ||
'dumi', | ||
); | ||
if (hasDumi) { | ||
collectCoverageFrom.push( | ||
'!src/.umi/**', | ||
'!src/.umi-test/**', | ||
'!src/.umi-production/**', | ||
); | ||
} | ||
|
||
writeFileSync( | ||
join(api.cwd, 'jest.config.ts'), | ||
` | ||
import { Config, createConfig } from '@umijs/test'; | ||
export default { | ||
...createConfig(),${ | ||
res.useRTL | ||
? ` | ||
setupFilesAfterEnv: ['<rootDir>/jest-setup.ts'],` | ||
: '' | ||
} | ||
collectCoverageFrom: [${collectCoverageFrom.map((v) => `'${v}'`).join(', ')}], | ||
} as Config.InitialOptions; | ||
`.trimStart(), | ||
); | ||
|
||
logger.info('Write jest.config.ts'); | ||
|
||
h.installDeps(); | ||
}, | ||
}); | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,65 @@ | ||
import { | ||
getNpmClient, | ||
installWithNpmClient, | ||
logger, | ||
prompts, | ||
} from '@umijs/utils'; | ||
import { writeFileSync } from 'fs'; | ||
import { IApi } from '../../types'; | ||
|
||
export class GeneratorHelper { | ||
constructor(readonly api: IApi) {} | ||
|
||
addDevDeps(deps: Record<string, string>) { | ||
const { api } = this; | ||
api.pkg.devDependencies = { | ||
...api.pkg.devDependencies, | ||
...deps, | ||
}; | ||
writeFileSync(api.pkgPath, JSON.stringify(api.pkg, null, 2)); | ||
logger.info('Write package.json'); | ||
} | ||
|
||
addScript(name: string, cmd: string) { | ||
const { api } = this; | ||
this.addScriptToPkg(name, cmd); | ||
writeFileSync(api.pkgPath, JSON.stringify(api.pkg, null, 2)); | ||
logger.info('Update package.json for scripts'); | ||
} | ||
|
||
private addScriptToPkg(name: string, cmd: string) { | ||
const { api } = this; | ||
const pkgScriptsName = api.pkg.scripts?.[name]; | ||
if (pkgScriptsName && pkgScriptsName !== cmd) { | ||
logger.warn( | ||
`scripts.${name} = "${pkgScriptsName}" already exists, will be overwritten with "${cmd}"!`, | ||
); | ||
} | ||
|
||
api.pkg.scripts = { | ||
...api.pkg.scripts, | ||
[name]: cmd, | ||
}; | ||
} | ||
|
||
installDeps() { | ||
const { api } = this; | ||
const npmClient = getNpmClient({ cwd: api.cwd }); | ||
installWithNpmClient({ | ||
npmClient, | ||
}); | ||
logger.info(`Install dependencies with ${npmClient}`); | ||
} | ||
} | ||
|
||
export function promptsExitWhenCancel<T extends string = string>( | ||
questions: prompts.PromptObject<T> | Array<prompts.PromptObject<T>>, | ||
options?: Pick<prompts.Options, 'onSubmit'>, | ||
): Promise<prompts.Answers<T>> { | ||
return prompts(questions, { | ||
...options, | ||
onCancel: () => { | ||
process.exit(1); | ||
}, | ||
}); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
{} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,87 @@ | ||
import { existsSync, readFileSync, unlinkSync, writeFileSync } from 'fs'; | ||
import path from 'path'; | ||
import * as cli from '../src/cli/cli'; | ||
import { GeneratorHelper } from '../src/commands/generators/utils'; | ||
|
||
let useRTL = false; | ||
jest.doMock('../src/commands/generators/utils', () => { | ||
const originalModule = jest.requireActual('../src/commands/generators/utils'); | ||
return { | ||
__esModule: true, | ||
...originalModule, | ||
promptsExitWhenCancel: jest.fn(() => ({ useRTL })), | ||
}; | ||
}); | ||
|
||
const mockInstall = jest.fn(); | ||
jest | ||
.spyOn(GeneratorHelper.prototype, 'installDeps') | ||
.mockImplementation(mockInstall); | ||
|
||
const CASES_DIR = path.join(__dirname, 'fixtures/generator'); | ||
describe('jest generator', function () { | ||
process.env.APP_ROOT = path.join(CASES_DIR); | ||
const jestConfPath = path.join(CASES_DIR, 'jest.config.ts'); | ||
const jestSetupPath = path.join(CASES_DIR, 'jest-setup.ts'); | ||
afterEach(() => { | ||
[jestConfPath, jestSetupPath].forEach((path) => { | ||
if (existsSync(path)) { | ||
unlinkSync(path); | ||
} | ||
}); | ||
writeFileSync(path.join(CASES_DIR, 'package.json'), '{}'); | ||
}); | ||
|
||
test('g jest', async () => { | ||
await cli.run({ | ||
args: { _: ['g', 'jest'], $0: 'node' }, | ||
}); | ||
|
||
const pkg = JSON.parse( | ||
readFileSync(path.join(CASES_DIR, 'package.json'), 'utf-8'), | ||
); | ||
|
||
expect(existsSync(jestConfPath)).toBeTruthy(); | ||
expect(pkg['scripts']).toMatchObject({ test: 'jest' }); | ||
expect(pkg['devDependencies']).toMatchObject({ | ||
jest: '^27', | ||
'@types/jest': '^27', | ||
typescript: '^4', | ||
'ts-node': '^10', | ||
'@umijs/test': '^4', | ||
}); | ||
expect(mockInstall).toBeCalled(); | ||
}); | ||
|
||
test('g jest with RTL', async () => { | ||
useRTL = true; | ||
|
||
await cli.run({ | ||
args: { _: ['g', 'jest'], $0: 'node' }, | ||
}); | ||
|
||
const pkg = JSON.parse( | ||
readFileSync(path.join(CASES_DIR, 'package.json'), 'utf-8'), | ||
); | ||
|
||
expect(existsSync(jestSetupPath)).toBeTruthy(); | ||
expect(pkg['scripts']).toMatchObject({ test: 'jest' }); | ||
expect(pkg['devDependencies']).toMatchObject({ | ||
'@testing-library/react': '^13', | ||
'@testing-library/jest-dom': '^5.16.4', | ||
'@types/testing-library__jest-dom': '^5.14.5', | ||
}); | ||
expect(mockInstall).toBeCalled(); | ||
}); | ||
|
||
test('warning when jest config exists', async () => { | ||
writeFileSync(jestConfPath, '{}'); | ||
const warnSpy = jest.spyOn(console, 'warn').mockImplementation(() => {}); | ||
await cli.run({ | ||
args: { _: ['g', 'jest'], $0: 'node' }, | ||
}); | ||
expect(warnSpy.mock.calls[0][1]).toBe( | ||
'Jest has already enabled. You can remove jest.config.{ts,js}, then run this again to re-setup.', | ||
); | ||
}); | ||
}); |