|
| 1 | +import fs from 'fs-extra' |
| 2 | +import path from 'path' |
| 3 | +import tmp from 'tmp' |
| 4 | + |
| 5 | +import execGitBase from '../src/execGit' |
1 | 6 | import runAll from '../src/runAll'
|
2 |
| -import resolveGitDir from '../src/resolveGitDir' |
3 | 7 |
|
4 |
| -jest.mock('../src/resolveGitDir') |
| 8 | +tmp.setGracefulCleanup() |
5 | 9 | jest.unmock('execa')
|
6 | 10 |
|
| 11 | +const testJsFilePretty = `module.exports = { |
| 12 | + foo: "bar" |
| 13 | +}; |
| 14 | +` |
| 15 | + |
| 16 | +const testJsFileUgly = `module.exports = { |
| 17 | + 'foo': 'bar', |
| 18 | +} |
| 19 | +` |
| 20 | + |
| 21 | +const testJsFileUnfixable = `const obj = { |
| 22 | + 'foo': 'bar' |
| 23 | +` |
| 24 | + |
| 25 | +let wcDir |
| 26 | +let cwd |
| 27 | + |
| 28 | +// Get file content |
| 29 | +const readFile = async (filename, dir = cwd) => |
| 30 | + fs.readFile(path.join(dir, filename), { encoding: 'utf-8' }) |
| 31 | + |
| 32 | +// Append to file, creating if it doesn't exist |
| 33 | +const appendFile = async (filename, content, dir = cwd) => |
| 34 | + fs.appendFile(path.join(dir, filename), content) |
| 35 | + |
| 36 | +// Wrap execGit to always pass `gitOps` |
| 37 | +const execGit = async args => execGitBase(args, { cwd }) |
| 38 | + |
| 39 | +// Execute runAll before git commit to emulate lint-staged |
| 40 | +const gitCommit = async options => { |
| 41 | + try { |
| 42 | + await runAll({ ...options, cwd, quiet: true }) |
| 43 | + await execGit(['commit', '-m "test"']) |
| 44 | + return true |
| 45 | + } catch (error) { |
| 46 | + return false |
| 47 | + } |
| 48 | +} |
| 49 | + |
7 | 50 | describe('runAll', () => {
|
8 | 51 | it('should throw when not in a git directory', async () => {
|
9 |
| - resolveGitDir.mockImplementationOnce(async () => null) |
10 |
| - await expect(runAll({})).rejects.toThrowErrorMatchingSnapshot() |
| 52 | + const nonGitDir = tmp.dirSync({ unsafeCleanup: true }) |
| 53 | + await expect(runAll({ cwd: nonGitDir })).rejects.toThrowErrorMatchingInlineSnapshot( |
| 54 | + `"Current directory is not a git directory!"` |
| 55 | + ) |
| 56 | + nonGitDir.removeCallback() |
| 57 | + }) |
| 58 | +}) |
| 59 | + |
| 60 | +describe('runAll', () => { |
| 61 | + beforeEach(async () => { |
| 62 | + wcDir = tmp.dirSync({ unsafeCleanup: true }) |
| 63 | + cwd = await fs.realpath(wcDir.name) |
| 64 | + |
| 65 | + // Init repository with initial commit |
| 66 | + await execGit('init') |
| 67 | + await execGit(['config', 'user.name', '"test"']) |
| 68 | + await execGit(['config', 'user.email', '"[email protected]"']) |
| 69 | + await appendFile('README.md', '# Test\n') |
| 70 | + await execGit(['add', 'README.md']) |
| 71 | + await execGit(['commit', '-m initial commit']) |
| 72 | + }) |
| 73 | + |
| 74 | + it('Should commit entire staged file when no errors from linter', async () => { |
| 75 | + // Stage pretty file |
| 76 | + await appendFile('test.js', testJsFilePretty) |
| 77 | + await execGit(['add', 'test.js']) |
| 78 | + |
| 79 | + // Run lint-staged with `prettier --list-different` and commit pretty file |
| 80 | + const success = await gitCommit({ config: { '*.js': 'prettier --list-different' } }) |
| 81 | + expect(success).toEqual(true) |
| 82 | + |
| 83 | + // Nothing is wrong, so a new commit is created |
| 84 | + expect(await execGit(['rev-list', '--count', 'HEAD'])).toEqual('2') |
| 85 | + expect(await execGit(['log', '-1', '--pretty=%B'])).toMatchInlineSnapshot(` |
| 86 | +" \\"test\\" |
| 87 | +" |
| 88 | +`) |
| 89 | + expect(await readFile('test.js')).toEqual(testJsFilePretty) |
| 90 | + }) |
| 91 | + |
| 92 | + it('Should commit entire staged file when no errors and linter modifies file', async () => { |
| 93 | + // Stage ugly file |
| 94 | + await appendFile('test.js', testJsFileUgly) |
| 95 | + await execGit(['add', 'test.js']) |
| 96 | + |
| 97 | + // Run lint-staged with `prettier --write` and commit pretty file |
| 98 | + const success = await gitCommit({ config: { '*.js': ['prettier --write', 'git add'] } }) |
| 99 | + expect(success).toEqual(true) |
| 100 | + |
| 101 | + // Nothing is wrong, so a new commit is created and file is pretty |
| 102 | + expect(await execGit(['rev-list', '--count', 'HEAD'])).toEqual('2') |
| 103 | + expect(await execGit(['log', '-1', '--pretty=%B'])).toMatchInlineSnapshot(` |
| 104 | +" \\"test\\" |
| 105 | +" |
| 106 | +`) |
| 107 | + expect(await readFile('test.js')).toEqual(testJsFilePretty) |
| 108 | + }) |
| 109 | + |
| 110 | + it('Should fail to commit entire staged file when errors from linter', async () => { |
| 111 | + // Stage ugly file |
| 112 | + await appendFile('test.js', testJsFileUgly) |
| 113 | + await execGit(['add', 'test.js']) |
| 114 | + const status = await execGit(['status']) |
| 115 | + |
| 116 | + // Run lint-staged with `prettier --list-different` to break the linter |
| 117 | + const success = await gitCommit({ config: { '*.js': 'prettier --list-different' } }) |
| 118 | + expect(success).toEqual(false) |
| 119 | + |
| 120 | + // Something was wrong so the repo is returned to original state |
| 121 | + expect(await execGit(['rev-list', '--count', 'HEAD'])).toEqual('1') |
| 122 | + expect(await execGit(['log', '-1', '--pretty=%B'])).toMatchInlineSnapshot(` |
| 123 | +" initial commit |
| 124 | +" |
| 125 | +`) |
| 126 | + expect(await execGit(['status'])).toEqual(status) |
| 127 | + expect(await readFile('test.js')).toEqual(testJsFileUgly) |
| 128 | + }) |
| 129 | + |
| 130 | + it('Should fail to commit entire staged file when errors from linter and linter modifies files', async () => { |
| 131 | + // Add unfixable file to commit so `prettier --write` breaks |
| 132 | + await appendFile('test.js', testJsFileUnfixable) |
| 133 | + await execGit(['add', 'test.js']) |
| 134 | + const status = await execGit(['status']) |
| 135 | + |
| 136 | + // Run lint-staged with `prettier --write` to break the linter |
| 137 | + const success = await gitCommit({ config: { '*.js': ['prettier --write', 'git add'] } }) |
| 138 | + expect(success).toEqual(false) |
| 139 | + |
| 140 | + // Something was wrong so the repo is returned to original state |
| 141 | + expect(await execGit(['rev-list', '--count', 'HEAD'])).toEqual('1') |
| 142 | + expect(await execGit(['log', '-1', '--pretty=%B'])).toMatchInlineSnapshot(` |
| 143 | +" initial commit |
| 144 | +" |
| 145 | +`) |
| 146 | + expect(await execGit(['status'])).toEqual(status) |
| 147 | + expect(await readFile('test.js')).toEqual(testJsFileUnfixable) |
| 148 | + }) |
| 149 | + |
| 150 | + it('Should commit partial change from partially staged file when no errors from linter', async () => { |
| 151 | + // Stage pretty file |
| 152 | + await appendFile('test.js', testJsFilePretty) |
| 153 | + await execGit(['add', 'test.js']) |
| 154 | + |
| 155 | + // Edit pretty file but do not stage changes |
| 156 | + const appended = '\nconsole.log("test");\n' |
| 157 | + await appendFile('test.js', appended) |
| 158 | + |
| 159 | + // Run lint-staged with `prettier --list-different` and commit pretty file |
| 160 | + const success = await gitCommit({ config: { '*.js': 'prettier --list-different' } }) |
| 161 | + expect(success).toEqual(true) |
| 162 | + |
| 163 | + // Nothing is wrong, so a new commit is created and file is pretty |
| 164 | + expect(await execGit(['rev-list', '--count', 'HEAD'])).toEqual('2') |
| 165 | + expect(await execGit(['log', '-1', '--pretty=%B'])).toMatchInlineSnapshot(` |
| 166 | +" \\"test\\" |
| 167 | +" |
| 168 | +`) |
| 169 | + |
| 170 | + // Latest commit contains pretty file |
| 171 | + // `git show` strips empty line from here here |
| 172 | + expect(await execGit(['show', 'HEAD:test.js'])).toEqual(testJsFilePretty.replace(/\n$/, '')) |
| 173 | + |
| 174 | + // Since edit was not staged, the file is still modified |
| 175 | + expect(await execGit(['status'])).toMatchInlineSnapshot(` |
| 176 | +"On branch master |
| 177 | +Changes not staged for commit: |
| 178 | + (use \\"git add <file>...\\" to update what will be committed) |
| 179 | + (use \\"git checkout -- <file>...\\" to discard changes in working directory) |
| 180 | +
|
| 181 | + modified: test.js |
| 182 | +
|
| 183 | +no changes added to commit (use \\"git add\\" and/or \\"git commit -a\\")" |
| 184 | +`) |
| 185 | + expect(await readFile('test.js')).toEqual(testJsFilePretty + appended) |
| 186 | + }) |
| 187 | + |
| 188 | + it('Should commit partial change from partially staged file when no errors from linter and linter modifies file', async () => { |
| 189 | + // Stage ugly file |
| 190 | + await appendFile('test.js', testJsFileUgly) |
| 191 | + await execGit(['add', 'test.js']) |
| 192 | + |
| 193 | + // Edit ugly file but do not stage changes |
| 194 | + const appended = '\n\nconsole.log("test");\n' |
| 195 | + await appendFile('test.js', appended) |
| 196 | + |
| 197 | + // Run lint-staged with `prettier --write` and commit pretty file |
| 198 | + const success = await gitCommit({ config: { '*.js': ['prettier --write', 'git add'] } }) |
| 199 | + expect(success).toEqual(true) |
| 200 | + |
| 201 | + // Nothing is wrong, so a new commit is created and file is pretty |
| 202 | + expect(await execGit(['rev-list', '--count', 'HEAD'])).toEqual('2') |
| 203 | + expect(await execGit(['log', '-1', '--pretty=%B'])).toMatchInlineSnapshot(` |
| 204 | +" \\"test\\" |
| 205 | +" |
| 206 | +`) |
| 207 | + |
| 208 | + // Latest commit contains pretty file |
| 209 | + // `git show` strips empty line from here here |
| 210 | + expect(await execGit(['show', 'HEAD:test.js'])).toEqual(testJsFilePretty.replace(/\n$/, '')) |
| 211 | + |
| 212 | + // Nothing is staged |
| 213 | + expect(await execGit(['status'])).toMatchInlineSnapshot(` |
| 214 | +"On branch master |
| 215 | +Changes not staged for commit: |
| 216 | + (use \\"git add <file>...\\" to update what will be committed) |
| 217 | + (use \\"git checkout -- <file>...\\" to discard changes in working directory) |
| 218 | +
|
| 219 | + modified: test.js |
| 220 | +
|
| 221 | +no changes added to commit (use \\"git add\\" and/or \\"git commit -a\\")" |
| 222 | +`) |
| 223 | + |
| 224 | + // File is pretty, and has been edited |
| 225 | + expect(await readFile('test.js')).toEqual(testJsFilePretty + appended) |
| 226 | + }) |
| 227 | + |
| 228 | + it('Should fail to commit partial change from partially staged file when errors from linter', async () => { |
| 229 | + // Stage ugly file |
| 230 | + await appendFile('test.js', testJsFileUgly) |
| 231 | + await execGit(['add', 'test.js']) |
| 232 | + |
| 233 | + // Edit ugly file but do not stage changes |
| 234 | + const appended = '\nconsole.log("test");\n' |
| 235 | + await appendFile('test.js', appended) |
| 236 | + const status = await execGit(['status']) |
| 237 | + |
| 238 | + // Run lint-staged with `prettier --list-different` to break the linter |
| 239 | + const success = await gitCommit({ config: { '*.js': 'prettier --list-different' } }) |
| 240 | + expect(success).toEqual(false) |
| 241 | + |
| 242 | + // Something was wrong so the repo is returned to original state |
| 243 | + expect(await execGit(['rev-list', '--count', 'HEAD'])).toEqual('1') |
| 244 | + expect(await execGit(['log', '-1', '--pretty=%B'])).toMatchInlineSnapshot(` |
| 245 | +" initial commit |
| 246 | +" |
| 247 | +`) |
| 248 | + expect(await execGit(['status'])).toEqual(status) |
| 249 | + expect(await readFile('test.js')).toEqual(testJsFileUgly + appended) |
| 250 | + }) |
| 251 | + |
| 252 | + it('Should fail to commit partial change from partially staged file when errors from linter and linter modifies files', async () => { |
| 253 | + // Add unfixable file to commit so `prettier --write` breaks |
| 254 | + await appendFile('test.js', testJsFileUnfixable) |
| 255 | + await execGit(['add', 'test.js']) |
| 256 | + |
| 257 | + // Edit unfixable file but do not stage changes |
| 258 | + const appended = '\nconsole.log("test");\n' |
| 259 | + await appendFile('test.js', appended) |
| 260 | + const status = await execGit(['status']) |
| 261 | + |
| 262 | + // Run lint-staged with `prettier --write` to break the linter |
| 263 | + const success = await gitCommit({ config: { '*.js': ['prettier --write', 'git add'] } }) |
| 264 | + expect(success).toEqual(false) |
| 265 | + |
| 266 | + // Something was wrong so the repo is returned to original state |
| 267 | + expect(await execGit(['rev-list', '--count', 'HEAD'])).toEqual('1') |
| 268 | + expect(await execGit(['log', '-1', '--pretty=%B'])).toMatchInlineSnapshot(` |
| 269 | +" initial commit |
| 270 | +" |
| 271 | +`) |
| 272 | + expect(await execGit(['status'])).toEqual(status) |
| 273 | + expect(await readFile('test.js')).toEqual(testJsFileUnfixable + appended) |
| 274 | + }) |
| 275 | + |
| 276 | + afterEach(async () => { |
| 277 | + wcDir.removeCallback() |
11 | 278 | })
|
12 | 279 | })
|
0 commit comments