Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Iiro Jäppinen
committed
Jun 20, 2019
1 parent
48f6145
commit 2942e1f
Showing
2 changed files
with
272 additions
and
27 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,36 @@ | ||
/* eslint no-underscore-dangle: 0 */ | ||
|
||
import path from 'path' | ||
import tmp from 'tmp' | ||
import execa from 'execa' | ||
|
||
import execGit from '../src/execGit' | ||
|
||
tmp.setGracefulCleanup() | ||
|
||
describe('gitWorkflow', () => { | ||
describe('execGit', () => { | ||
it('should execute git in process.cwd if working copy is not specified', async () => { | ||
await execGit(['init', 'param']) | ||
expect(execa).toHaveBeenCalledWith('git', ['init', 'param'], { | ||
cwd: path.resolve(process.cwd()) | ||
}) | ||
}) | ||
|
||
it('should execute git in a given working copy', async () => { | ||
await execGit(['init', 'param'], { cwd: 'test/__fixtures__' }) | ||
expect(execa).toHaveBeenCalledWith('git', ['init', 'param'], { | ||
cwd: path.resolve(process.cwd(), 'test', '__fixtures__') | ||
}) | ||
}) | ||
|
||
it('should work with relative paths', async () => { | ||
await execGit(['init', 'param'], { | ||
cwd: 'test/__fixtures__' | ||
}) | ||
expect(execa).toHaveBeenCalledWith('git', ['init', 'param'], { | ||
cwd: path.resolve(process.cwd(), 'test', '__fixtures__') | ||
}) | ||
}) | ||
}) | ||
}) |
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 |
---|---|---|
@@ -1,35 +1,244 @@ | ||
/* eslint no-underscore-dangle: 0 */ | ||
|
||
import execa from 'execa' | ||
import fs from 'fs' | ||
import path from 'path' | ||
import pify from 'pify' | ||
import tmp from 'tmp' | ||
import execa from 'execa' | ||
import { execGit } from '../src/gitWorkflow' | ||
|
||
import gitWorkflow from '../src/gitWorkflow' | ||
|
||
const fsp = pify(fs) | ||
|
||
tmp.setGracefulCleanup() | ||
jest.unmock('execa') | ||
|
||
const testJsFilePretty = `module.exports = { | ||
foo: "bar" | ||
}; | ||
` | ||
|
||
const testJsFileUgly = `module.exports = { | ||
'foo': 'bar', | ||
} | ||
` | ||
|
||
const testJsFileUnfixable = `const obj = { | ||
'foo': 'bar' | ||
` | ||
|
||
let wcDir | ||
let wcDirPath | ||
let gitOpts | ||
|
||
// Get file content | ||
const readFile = async (filename, dir = wcDirPath) => | ||
fsp.readFile(path.join(dir, filename), { encoding: 'utf-8' }) | ||
|
||
// Append to file, creating if it doesn't exist | ||
const appendFile = async (filename, content, dir = wcDirPath) => { | ||
await fsp.appendFile(path.join(dir, filename), content) | ||
} | ||
|
||
// Wrap execGit to always pass `gitOps` | ||
const execGit = async args => gitWorkflow.execGit(args, gitOpts) | ||
|
||
// Emulate lint-staged in pre-commit-hook | ||
const runLintStaged = async (fix = false) => { | ||
// First save backup | ||
await gitWorkflow.saveStagedFiles(gitOpts) | ||
|
||
try { | ||
if (fix) { | ||
// Run prettier with --write | ||
await execa('prettier', ['--write', 'test.js'], { cwd: wcDirPath }) | ||
// add files | ||
await execGit(['add', 'test.js']) | ||
} else { | ||
// Run prettier | ||
await execa('prettier', ['--list-different', 'test.js'], { cwd: wcDirPath }) | ||
} | ||
|
||
// commit | ||
await execGit(['commit', '-m test']) | ||
} catch (error) { | ||
// in case of linter error restore backup | ||
await gitWorkflow.restoreStagedFiles(gitOpts) | ||
} | ||
|
||
await gitWorkflow.clearStagedFileStash(gitOpts) | ||
} | ||
|
||
describe('gitWorkflow', () => { | ||
describe('execGit', () => { | ||
it('should execute git in process.cwd if working copy is not specified', async () => { | ||
await execGit(['init', 'param']) | ||
expect(execa).toHaveBeenCalledWith('git', ['init', 'param'], { | ||
cwd: path.resolve(process.cwd()) | ||
}) | ||
}) | ||
|
||
it('should execute git in a given working copy', async () => { | ||
await execGit(['init', 'param'], { cwd: 'test/__fixtures__' }) | ||
expect(execa).toHaveBeenCalledWith('git', ['init', 'param'], { | ||
cwd: path.resolve(process.cwd(), 'test', '__fixtures__') | ||
}) | ||
}) | ||
|
||
it('should work with relative paths', async () => { | ||
await execGit(['init', 'param'], { | ||
cwd: 'test/__fixtures__' | ||
}) | ||
expect(execa).toHaveBeenCalledWith('git', ['init', 'param'], { | ||
cwd: path.resolve(process.cwd(), 'test', '__fixtures__') | ||
}) | ||
}) | ||
beforeEach(async () => { | ||
wcDir = tmp.dirSync({ unsafeCleanup: true }) | ||
wcDirPath = wcDir.name | ||
gitOpts = { cwd: wcDirPath } | ||
|
||
// Init repository with initial commit | ||
await execGit('init') | ||
await execGit(['config', 'user.name', '"test"']) | ||
await execGit(['config', 'user.email', '"test@test.com"']) | ||
await appendFile('README.md', '# Test\n') | ||
await execGit(['add', 'README.md']) | ||
await execGit(['commit', '-m initial commit']) | ||
}) | ||
|
||
it('Should commit file when no errors from linter', async () => { | ||
// Stage pretty file | ||
await appendFile('test.js', testJsFilePretty) | ||
await execGit(['add', 'test.js']) | ||
|
||
// Run lint-staged with `prettier --list-different` and commit pretty file | ||
await runLintStaged() | ||
|
||
// Nothing is wrong, so a new commit is created | ||
expect(await execGit(['rev-list', '--count', 'HEAD'])).toEqual('2') | ||
expect(await execGit(['log', '-1', '--pretty=%B'])).toMatch('test') | ||
expect(await readFile('test.js')).toEqual(testJsFilePretty) | ||
}) | ||
|
||
it('Should commit file when no errors and linter modifies file', async () => { | ||
// Stage ugly file | ||
await appendFile('test.js', testJsFileUgly) | ||
await execGit(['add', 'test.js']) | ||
|
||
// Run lint-staged with `prettier --write` and commit pretty file | ||
await runLintStaged(true) | ||
|
||
// Nothing is wrong, so a new commit is created and file is pretty | ||
expect(await execGit(['rev-list', '--count', 'HEAD'])).toEqual('2') | ||
expect(await execGit(['log', '-1', '--pretty=%B'])).toMatch('test') | ||
expect(await readFile('test.js')).toEqual(testJsFilePretty) | ||
}) | ||
|
||
it('Should fail to commit file when errors from linter', async () => { | ||
// Stage ugly file | ||
await appendFile('test.js', testJsFileUgly) | ||
await execGit(['add', 'test.js']) | ||
const status = await execGit(['status']) | ||
|
||
// Run lint-staged with `prettier --list-different` to break the linter | ||
await runLintStaged() | ||
|
||
// Something was wrong so the repo is returned to original state | ||
expect(await execGit(['rev-list', '--count', 'HEAD'])).toEqual('1') | ||
expect(await execGit(['log', '-1', '--pretty=%B'])).toMatch('initial commit') | ||
expect(await execGit(['status'])).toEqual(status) | ||
expect(await readFile('test.js')).toEqual(testJsFileUgly) | ||
}) | ||
|
||
it('Should fail to commit file when errors from linter and linter modifies files', async () => { | ||
// Add unfixable file to commit so `prettier --write` breaks | ||
await appendFile('test.js', testJsFileUnfixable) | ||
await execGit(['add', 'test.js']) | ||
const status = await execGit(['status']) | ||
|
||
// Run lint-staged with `prettier --write` to break the linter | ||
await runLintStaged(true) | ||
|
||
// Something was wrong so the repo is returned to original state | ||
expect(await execGit(['rev-list', '--count', 'HEAD'])).toEqual('1') | ||
expect(await execGit(['log', '-1', '--pretty=%B'])).toMatch('initial commit') | ||
expect(await execGit(['status'])).toEqual(status) | ||
expect(await readFile('test.js')).toEqual(testJsFileUnfixable) | ||
}) | ||
|
||
it('Should commit partial change when no errors from linter', async () => { | ||
// Stage pretty file | ||
await appendFile('test.js', testJsFilePretty) | ||
await execGit(['add', 'test.js']) | ||
|
||
// Edit pretty file but do not stage changes | ||
const appended = '\nconsole.log("test");\n' | ||
await appendFile('test.js', appended) | ||
|
||
// Run lint-staged with `prettier --list-different` and commit pretty file | ||
await runLintStaged() | ||
|
||
// Nothing is wrong, so a new commit is created and file is pretty | ||
expect(await execGit(['rev-list', '--count', 'HEAD'])).toEqual('2') | ||
expect(await execGit(['log', '-1', '--pretty=%B'])).toMatch('test') | ||
|
||
// Latest commit contains pretty file | ||
// `git show` strips empty line from here here | ||
expect(await execGit(['show', 'HEAD:test.js'])).toMatch(testJsFilePretty.replace(/\n$/, '')) | ||
|
||
// Since edit was not staged, the file is still modified | ||
expect(await execGit(['status'])).toMatch('modified: test.js') | ||
expect(await readFile('test.js')).toMatch(testJsFilePretty + appended) | ||
}) | ||
|
||
it('Should commit entire file on partial change when no errors from linter and linter modifies file', async () => { | ||
// Stage ugly file | ||
await appendFile('test.js', testJsFileUgly) | ||
await execGit(['add', 'test.js']) | ||
|
||
// Edit ugly file but do not stage changes | ||
const appended = '\nconsole.log("test");\n' | ||
await appendFile('test.js', appended) | ||
|
||
// Run lint-staged with `prettier --list-different` and commit pretty file | ||
await runLintStaged(true) | ||
|
||
// Nothing is wrong, so a new commit is created and file is pretty | ||
expect(await execGit(['rev-list', '--count', 'HEAD'])).toEqual('2') | ||
expect(await execGit(['log', '-1', '--pretty=%B'])).toMatch('test') | ||
|
||
// Latest commit contains pretty file with edits | ||
// `git show` strips empty line from here here | ||
expect(await execGit(['show', 'HEAD:test.js'])).toMatch( | ||
(testJsFilePretty + appended).replace(/\n$/, '') | ||
) | ||
|
||
// Nothing is staged | ||
expect(await execGit(['status'])).toMatch( | ||
'On branch master\nnothing to commit, working tree clean' | ||
) | ||
|
||
// File is pretty, and has been edited | ||
expect(await readFile('test.js')).toMatch(testJsFilePretty + appended) | ||
}) | ||
|
||
it('Should fail to partial change when errors from linter', async () => { | ||
// Stage ugly file | ||
await appendFile('test.js', testJsFileUgly) | ||
await execGit(['add', 'test.js']) | ||
|
||
// Edit ugly file but do not stage changes | ||
const appended = '\nconsole.log("test");\n' | ||
await appendFile('test.js', appended) | ||
const status = await execGit(['status']) | ||
|
||
// Run lint-staged with `prettier --list-different` to break the linter | ||
await runLintStaged() | ||
|
||
// Something was wrong so the repo is returned to original state | ||
expect(await execGit(['rev-list', '--count', 'HEAD'])).toEqual('1') | ||
expect(await execGit(['log', '-1', '--pretty=%B'])).toMatch('initial commit') | ||
expect(await execGit(['status'])).toEqual(status) | ||
expect(await readFile('test.js')).toMatch(testJsFileUgly + appended) | ||
}) | ||
|
||
it('Should fail to commit partial change when errors from linter and linter modifies files', async () => { | ||
// Add unfixable file to commit so `prettier --write` breaks | ||
await appendFile('test.js', testJsFileUnfixable) | ||
await execGit(['add', 'test.js']) | ||
|
||
// Edit unfixable file but do not stage changes | ||
const appended = '\nconsole.log("test");\n' | ||
await appendFile('test.js', appended) | ||
const status = await execGit(['status']) | ||
|
||
// Run lint-staged with `prettier --write` to break the linter | ||
await runLintStaged(true) | ||
|
||
// Something was wrong so the repo is returned to original state | ||
expect(await execGit(['rev-list', '--count', 'HEAD'])).toEqual('1') | ||
expect(await execGit(['log', '-1', '--pretty=%B'])).toMatch('initial commit') | ||
expect(await execGit(['status'])).toEqual(status) | ||
expect(await readFile('test.js')).toMatch(testJsFileUnfixable + appended) | ||
}) | ||
|
||
afterEach(async () => { | ||
wcDir.removeCallback() | ||
}) | ||
}) |