├── .editorconfig ├── .gitattributes ├── .gitignore ├── .travis.yml ├── appveyor.yml ├── index.js ├── license ├── package.json ├── readme.md └── test.js /.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | 3 | [*] 4 | indent_style = tab 5 | end_of_line = lf 6 | charset = utf-8 7 | trim_trailing_whitespace = true 8 | insert_final_newline = true 9 | 10 | [{package.json,*.yml}] 11 | indent_style = space 12 | indent_size = 2 13 | 14 | [*.md] 15 | trim_trailing_whitespace = false 16 | -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | * text=auto 2 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: node_js 2 | node_js: 3 | - '9' 4 | - '8' 5 | - '6' 6 | - '4' 7 | -------------------------------------------------------------------------------- /appveyor.yml: -------------------------------------------------------------------------------- 1 | environment: 2 | matrix: 3 | - nodejs_version: '9' 4 | - nodejs_version: '8' 5 | - nodejs_version: '6' 6 | - nodejs_version: '4' 7 | install: 8 | - ps: Install-Product node $env:nodejs_version 9 | - set CI=true 10 | - npm install -g npm@latest 11 | - npm install 12 | build: off 13 | test_script: 14 | - node --version 15 | - npm --version 16 | - npm test -- --serial 17 | -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | const mockBin = require('mock-bin'); 3 | const which = require('shelljs').which; 4 | 5 | const rbackslash = /\\/g; 6 | 7 | module.exports = function (js, command) { 8 | const originalGit = which('git').stdout.replace(rbackslash, '\\\\'); 9 | 10 | if (command) { 11 | js = 'var argv = process.argv;' + 12 | 'var commandExists = false;' + 13 | 'var length = argv.length;' + 14 | 'while (--length) {' + 15 | 'if (argv[length] === "' + command + '") {' + 16 | 'commandExists = true;' + 17 | 'break;' + 18 | '}' + 19 | '}' + 20 | 'if (commandExists) {' + 21 | js + 22 | '} else {' + 23 | 'function handleExitCode(code) { process.exitCode = code || 0; }' + 24 | 'var childProcess = require("child_process");' + 25 | 'argv.shift();' + 26 | 'argv.shift();' + 27 | 'childProcess.spawn("' + originalGit + '", argv, {' + 28 | 'stdio: "inherit"' + 29 | '}).on("close", handleExitCode).on("exit", handleExitCode);' + 30 | '}'; 31 | } 32 | 33 | return mockBin('git', 'node', js); 34 | }; 35 | -------------------------------------------------------------------------------- /license: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) Steve Mao (github.com/stevemao) 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in 13 | all copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | THE SOFTWARE. 22 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "mock-git", 3 | "version": "2.0.0", 4 | "description": "Mock any git command", 5 | "license": "MIT", 6 | "repository": "stevemao/mock-git", 7 | "author": { 8 | "name": "Steve Mao", 9 | "email": "maochenyan@gmail.com", 10 | "url": "github.com/stevemao" 11 | }, 12 | "engines": { 13 | "node": ">=4" 14 | }, 15 | "scripts": { 16 | "test": "xo && ava" 17 | }, 18 | "files": [ 19 | "index.js" 20 | ], 21 | "keywords": [ 22 | "mock", 23 | "test", 24 | "git", 25 | "executable" 26 | ], 27 | "dependencies": { 28 | "mock-bin": "^2.0.0", 29 | "shelljs": "^0.7.0" 30 | }, 31 | "devDependencies": { 32 | "ava": "^0.24.0", 33 | "xo": "^0.18.2" 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /readme.md: -------------------------------------------------------------------------------- 1 | # mock-git [![Build Status: Linux](https://travis-ci.org/stevemao/mock-git.svg?branch=master)](https://travis-ci.org/stevemao/mock-git) [![Build Status: Windows](https://ci.appveyor.com/api/projects/status/76bg3fbqo2ox9s11/branch/master?svg=true)](https://ci.appveyor.com/project/stevemao/mock-git/branch/master) 2 | 3 | > Mock any git command 4 | 5 | Useful for mocking tests that run git command, especially to fake edge cases and increase test coverage. 6 | 7 | 8 | ## Install 9 | 10 | ``` 11 | $ npm install --save-dev mock-git 12 | ``` 13 | 14 | 15 | ## Usage 16 | 17 | ```js 18 | const mockGit = require('mock-git'); 19 | const log = 'mocking git bla!'; 20 | const unmock = await mockGit(`console.log('${log}')`, 'bla'); 21 | let actual = shell.exec('git bla').stdout; 22 | t.is(log + '\n', actual); 23 | 24 | actual = shell.exec('git').stdout; 25 | t.not(log + '\n', actual); 26 | 27 | unmock(); 28 | actual = shell.exec('git bla').stdout; 29 | t.not(log + '\n', actual); 30 | ``` 31 | 32 | Checkout the [tests](./test.js) for more details 33 | 34 | 35 | ## API 36 | 37 | ### mockGit(js, [command]) 38 | 39 | Returns a promise which resolves with an unmock function. 40 | 41 | ##### js 42 | 43 | Type: `string` 44 | 45 | Nodejs code. 46 | 47 | #### command 48 | 49 | Type: `string` 50 | 51 | EG: `'commit'`. 52 | 53 | If omitted, it will mock the git binary. 54 | 55 | 56 | ## Related 57 | 58 | - [mock-bin](https://github.com/stevemao/mock-bin) - Mock any executable binary 59 | 60 | 61 | ## License 62 | 63 | MIT © [Steve Mao](https://github.com/stevemao) 64 | -------------------------------------------------------------------------------- /test.js: -------------------------------------------------------------------------------- 1 | import test from 'ava'; 2 | import shell from 'shelljs'; 3 | import m from './'; 4 | 5 | shell.config.silent = true; 6 | 7 | test('mock and unmock git bla', async t => { 8 | const log = 'mocking git bla!'; 9 | const unmock = await m(`console.log('${log}')`, 'bla'); 10 | let actual = shell.exec('git bla').stdout; 11 | t.is(log + '\n', actual); 12 | 13 | actual = shell.exec('git').stdout; 14 | t.not(log + '\n', actual); 15 | 16 | unmock(); 17 | actual = shell.exec('git bla').stdout; 18 | t.not(log + '\n', actual); 19 | }); 20 | 21 | test('mock and unmock git --no-pager bla', async t => { 22 | const log = 'mocking git bla!'; 23 | const unmock = await m(`console.log('${log}')`, 'bla'); 24 | let actual = shell.exec('git --no-pager bla').stdout; 25 | t.is(log + '\n', actual); 26 | 27 | actual = shell.exec('git').stdout; 28 | t.not(log + '\n', actual); 29 | 30 | unmock(); 31 | actual = shell.exec('git --no-pager bla').stdout; 32 | t.not(log + '\n', actual); 33 | }); 34 | 35 | test('mocking bar does not affect foo', async t => { 36 | const fooLog = 'mocking foo!'; 37 | await m(`console.log('${fooLog}')`, 'foo'); 38 | 39 | const barLog = 'mocking bar!'; 40 | await m(`console.log('${barLog}')`, 'bar'); 41 | 42 | let barActual = shell.exec('git bar').stdout; 43 | t.is(barLog + '\n', barActual); 44 | 45 | barActual = shell.exec('git --no-pager bar').stdout; 46 | t.is(barLog + '\n', barActual); 47 | 48 | let fooActual = shell.exec('git foo').stdout; 49 | t.is(fooLog + '\n', fooActual); 50 | 51 | fooActual = shell.exec('git --no-pager foo').stdout; 52 | t.is(fooLog + '\n', fooActual); 53 | 54 | const stderr = shell.exec('git log').stderr; 55 | t.falsy(stderr); 56 | }); 57 | 58 | test('mocking git', async t => { 59 | const log = 'mocking git!'; 60 | const unmock = await m(`console.log('${log}')`); 61 | 62 | let actual = shell.exec('git'); 63 | t.is(log + '\n', actual.stdout); 64 | t.falsy(actual.stderr); 65 | 66 | actual = shell.exec('git foo'); 67 | t.is(log + '\n', actual.stdout); 68 | t.falsy(actual.stderr); 69 | 70 | actual = shell.exec('git --no-pager log'); 71 | t.is(log + '\n', actual.stdout); 72 | t.falsy(actual.stderr); 73 | 74 | unmock(); 75 | actual = shell.exec('git'); 76 | t.not(log + '\n', actual.stdout); 77 | t.falsy(actual.stderr); 78 | }); 79 | 80 | test('passing arguments while mocking only commit', async t => { 81 | const unmock = await m(`console.log(process.argv.splice(2).join(' '));`, 'commit'); 82 | const args = 'commit --obviously-invalid-arg -m "second commit with spaces!"'; 83 | 84 | const actual = shell.exec(`git ${args}`); 85 | t.falsy(actual.stderr); 86 | t.is(`${args.replace(new RegExp('"', 'g'), '')}\n`, actual.stdout); 87 | 88 | unmock(); 89 | }); 90 | 91 | test('passing arguments while mocking whole git', async t => { 92 | const unmock = await m(`console.log(process.argv.splice(2).join(' '));`); 93 | const args = 'commit --obviously-invalid-arg -m "third commit with spaces!"'; 94 | 95 | const actual = shell.exec(`git ${args}`); 96 | t.falsy(actual.stderr); 97 | t.is(`${args.replace(new RegExp('"', 'g'), '')}\n`, actual.stdout); 98 | 99 | unmock(); 100 | }); 101 | 102 | test('passing through exit code', async t => { 103 | const unmock = await m('process.exitCode = 1'); 104 | const args = 'foo'; 105 | 106 | const actual = shell.exec(`git ${args}`).code; 107 | t.is(1, actual); 108 | 109 | unmock(); 110 | }); 111 | 112 | test('passing through exit code with multiple mocks', async t => { 113 | const unmock = [await m('process.exitCode = 1', 'one'), await m('process.exitCode = 2', 'two')]; 114 | 115 | t.is(1, shell.exec(`git one`).code); 116 | t.is(2, shell.exec(`git two`).code); 117 | 118 | unmock[0](); 119 | unmock[1](); 120 | }); 121 | --------------------------------------------------------------------------------