diff --git a/src/tests/add/dep.spec.ts b/src/tests/add/dep.spec.ts index d3b2244..c96a06e 100644 --- a/src/tests/add/dep.spec.ts +++ b/src/tests/add/dep.spec.ts @@ -1,145 +1,99 @@ -import { filesystem, system } from 'gluegun'; -const stripANSI = require('strip-ansi'); +import { filesystem } from 'gluegun'; import * as latestVersion from 'latest-version'; import Dep from '../../commands/add/dep'; +import { + createDefaultNbxConfig, + expectFailCli, + expectGitCommits, + testCli, +} from '../test-helpers.spec'; jest.setTimeout(90000); -/* eslint-disable no-console,max-nested-callbacks,@typescript-eslint/ban-ts-ignore */ -let consoleLogOutput: string[]; -let execPath: string; -const originalCwd = process.cwd(); -const originalLog = console.log; +testCli({ + runCommand: (args: string[]) => Dep.run(args), + name: 'nbx add:dep', + defaultArgs: ['--no-spinner'], + tests: [ + { + name: 'fail if no package.json found', + runner: expectFailCli({ + args: ['chalk'], + errorMessage: 'There is no package.json not found in the current folder', + before: async () => { + filesystem.remove('package.json'); + }, + }), + }, + { + name: 'fail if dependancy is already in package.json', + runner: expectFailCli({ + args: ['chalk'], + errorMessage: 'chalk is already installed in this project', + before: async () => { + await createDefaultNbxConfig(); + filesystem.write('package.json', { dependencies: { chalk: '2.3.2' } }); + }, + }), + }, + { + name: 'fail if dev dependancy is already in package.json', + runner: expectFailCli({ + args: ['chalk', '-D'], + errorMessage: 'chalk is already installed in this project', + before: async () => { + await createDefaultNbxConfig(); + filesystem.write('package.json', { devDependencies: { chalk: '2.3.2' } }); + }, + }), + }, + { + name: 'work with dependency', + runner: expectGitCommits({ + args: ['chalk'], + expectGitLog: async () => { + const latestChalk = await latestVersion('chalk'); + return [ + `:heavy_plus_sign: add chalk@${latestChalk} as a dependency`, + 'M\tpackage.json', + 'M\tyarn.lock', + ]; + }, + checks: async () => { + const latestChalk = await latestVersion('chalk'); -beforeEach(async () => { - consoleLogOutput = []; - console.log = (x: any) => consoleLogOutput.push(stripANSI(x)); + const packageJson = filesystem.read('package.json', 'json'); + expect(packageJson).toStrictEqual({ + dependencies: { + chalk: latestChalk, + }, + }); + }, + }), + }, + { + name: 'work with a dev dependency', + runner: expectGitCommits({ + args: ['chalk', '-D'], + expectGitLog: async () => { + const latestChalk = await latestVersion('chalk'); + return [ + `:heavy_plus_sign: add chalk@${latestChalk} as a dev dependency`, + 'M\tpackage.json', + 'M\tyarn.lock', + ]; + }, + checks: async () => { + const latestChalk = await latestVersion('chalk'); - const tmpDirName = `${new Date().getTime()}-${Math.random() * 100}-add-nbx-test`; - execPath = filesystem.path('/', 'tmp', tmpDirName); - filesystem.dir(execPath); - process.chdir(execPath); -}); -afterEach(() => { - console.log = originalLog; - process.chdir(originalCwd); - jest.restoreAllMocks(); - filesystem.remove(execPath); -}); - -const initWithConfigAndCommit = async () => { - await system.run('git init'); - filesystem.write('.nbxrc', { git: { user: 'aaa', email: 'bbb' } }); - filesystem.write('package.json', {}); - await system.run('touch yarn.lock'); - await system.run('echo node_modules > .gitignore'); - await system.run('git add * .nbxrc .gitignore'); - try { - await system.run('git config user.email "you@example.com"'); - await system.run('git config user.name "Your Name"'); - } catch {} - await system.run('git commit -m "init state"'); -}; - -describe('dep', () => { - it('should print help correctly', async () => { - try { - await Dep.run(['-h']); - } catch {} - expect(consoleLogOutput).toMatchSnapshot(); - }); - it('should fail if no package.json found', async () => { - expect.assertions(1); - try { - filesystem.remove('package.json'); - await Dep.run(['chalk', '--no-spinner']); - // eslint-disable-next-line unicorn/catch-error-name - } catch (e) { - expect(e).toMatchSnapshot(); - } - }); - it('should fail if dependancy is already in package.json', async () => { - expect.assertions(1); - try { - filesystem.write('package.json', { dependencies: { chalk: '2.3.2' } }); - await Dep.run(['chalk', '--no-spinner']); - // eslint-disable-next-line unicorn/catch-error-name - } catch (e) { - expect(e).toMatchSnapshot(); - } - }); - it('should fail if dev dependancy is already in package.json', async () => { - expect.assertions(1); - try { - filesystem.write('package.json', { devDependencies: { chalk: '2.3.2' } }); - await Dep.run(['chalk', '--dev', '--no-spinner']); - // eslint-disable-next-line unicorn/catch-error-name - } catch (e) { - expect(e).toMatchSnapshot(); - } - }); - - it('should work with dependency', async () => { - await initWithConfigAndCommit(); - const latestChalk = await latestVersion('chalk'); - - await Dep.run(['chalk', '--no-spinner']); - - const packageJson = filesystem.read('package.json', 'json'); - expect(consoleLogOutput).toMatchSnapshot(); - expect(packageJson).toStrictEqual({ - dependencies: { - chalk: latestChalk, - }, - }); - const after = await system.run('git log --name-status --format="%s"'); - const afterSlitted = after - .split('\n') - .map(val => val.trim()) - .map(val => stripANSI(val)) - .filter(val => val !== ''); - - expect(afterSlitted).toStrictEqual([ - `:heavy_plus_sign: add chalk@${latestChalk} as a dependency`, - 'M\tpackage.json', - 'M\tyarn.lock', - 'init state', - 'A\t.gitignore', - 'A\t.nbxrc', - 'A\tpackage.json', - 'A\tyarn.lock', - ]); - }); - - it('should work with a dev dependency', async () => { - await initWithConfigAndCommit(); - const latestChalk = await latestVersion('chalk'); - - await Dep.run(['chalk', '-D', '--no-spinner']); - - const packageJson = filesystem.read('package.json', 'json'); - expect(consoleLogOutput).toMatchSnapshot(); - expect(packageJson).toStrictEqual({ - devDependencies: { - chalk: latestChalk, - }, - }); - const after = await system.run('git log --name-status --format="%s"'); - const afterSlitted = after - .split('\n') - .map(val => val.trim()) - .map(val => stripANSI(val)) - .filter(val => val !== ''); - - expect(afterSlitted).toStrictEqual([ - `:heavy_plus_sign: add chalk@${latestChalk} as a dev dependency`, - 'M\tpackage.json', - 'M\tyarn.lock', - 'init state', - 'A\t.gitignore', - 'A\t.nbxrc', - 'A\tpackage.json', - 'A\tyarn.lock', - ]); - }); + const packageJson = filesystem.read('package.json', 'json'); + expect(packageJson).toStrictEqual({ + devDependencies: { + chalk: latestChalk, + }, + }); + }, + }), + }, + ], }); diff --git a/src/tests/test-helpers.spec.ts b/src/tests/test-helpers.spec.ts new file mode 100644 index 0000000..dcb1c26 --- /dev/null +++ b/src/tests/test-helpers.spec.ts @@ -0,0 +1,153 @@ +import { Command } from '@oclif/command'; +const stripANSI = require('strip-ansi'); +import { filesystem, system } from 'gluegun'; + +export type TestRun = (args: { exec: Function; defaultArgs?: string[] }) => Promise; + +export interface TestCase { + name: string; + runner: TestRun; +} + +export interface TestCliParams { + tests: TestCase[]; + runCommand: (args: string[]) => PromiseLike; + name: string; + defaultArgs?: string[]; +} + +export const createDefaultNbxConfig = async () => { + filesystem.write('.nbxrc', { git: { user: 'aaa', email: 'bbb' } }); +}; + +export const initWithConfigAndCommit = async () => { + await system.run('git init'); + await createDefaultNbxConfig(); + filesystem.write('package.json', {}); + await system.run('touch yarn.lock'); + await system.run('echo node_modules > .gitignore'); + await system.run('git add * .nbxrc .gitignore'); + try { + await system.run('git config user.email "you@example.com"'); + await system.run('git config user.name "Your Name"'); + } catch {} + await system.run('git commit -m "init state"'); +}; + +export const expectGitCommits = ({ + before, + args, + expectGitLog, + checks, +}: { + args: string[]; + expectGitLog: string[] | (() => Promise); + before?: () => Promise; + checks?: () => Promise; +}): TestRun => async ({ exec, defaultArgs = [] }) => { + await initWithConfigAndCommit(); + await before?.(); + + try { + await exec([...args, ...defaultArgs]); + } catch { + fail('Cli errored'); + } + + const after = await system.run('git log --name-status --format="%s"'); + const afterSlitted = after + .split('\n') + .map(val => val.trim()) + .map(val => stripANSI(val)) + .filter(val => val !== ''); + + if (expectGitLog instanceof Function) { + expect(afterSlitted).toStrictEqual([ + ...(await expectGitLog()), + 'init state', + 'A\t.gitignore', + 'A\t.nbxrc', + 'A\tpackage.json', + 'A\tyarn.lock', + ]); + } else { + expect(afterSlitted).toStrictEqual([ + ...expectGitLog, + 'init state', + 'A\t.gitignore', + 'A\t.nbxrc', + 'A\tpackage.json', + 'A\tyarn.lock', + ]); + } + + await checks?.(); +}; + +export const expectFailCli = ({ + assertions = 4, + before, + args, + errorMessage, +}: { + assertions?: number; + before?: () => Promise; + args: string[]; + errorMessage: string; +}): TestRun => async ({ exec, defaultArgs = [] }) => { + expect.assertions(assertions); + try { + await before?.(); + + await exec([...args, ...defaultArgs]); + // eslint-disable-next-line unicorn/catch-error-name + } catch (e) { + expect(e).toBeDefined(); + expect(e.message).toBeDefined(); + expect(e.message).toBe(errorMessage); + } +}; + +export const testCli = ({ tests, runCommand, name, defaultArgs }: TestCliParams) => { + /* eslint-disable no-console,max-nested-callbacks,@typescript-eslint/ban-ts-ignore */ + let consoleLogOutput: string[]; + let execPath: string; + const originalCwd = process.cwd(); + const originalLog = console.log; + + beforeEach(async () => { + consoleLogOutput = []; + console.log = (x: any) => consoleLogOutput.push(stripANSI(x)); + + const tmpDirName = `${new Date().getTime()}-${Math.random() * 100}-add-nbx-test`; + execPath = filesystem.path('/', 'tmp', tmpDirName); + filesystem.dir(execPath); + process.chdir(execPath); + }); + afterEach(() => { + console.log = originalLog; + process.chdir(originalCwd); + jest.restoreAllMocks(); + filesystem.remove(execPath); + }); + + describe(name, () => { + it('should print help correctly', async () => { + try { + await runCommand(['-h']); + } catch {} + expect(consoleLogOutput).toMatchSnapshot(); + }); + + tests.forEach(testCase => { + it(`should ${testCase.name}`, async () => { + await testCase.runner({ exec: runCommand, defaultArgs: defaultArgs }); + expect(consoleLogOutput).toMatchSnapshot(); + }); + }); + }); +}; + +const testHelp = (command: Command) => { + it(''); +};