@iamandersonp/prettier-staged 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,295 @@
1
+ const fs = require('node:fs');
2
+ const path = require('node:path');
3
+ const { execSync } = require('node:child_process');
4
+
5
+ // Mock del módulo fs y child_process
6
+ jest.mock('node:fs');
7
+ jest.mock('node:child_process');
8
+
9
+ const {
10
+ isExternalInstallation,
11
+ getTargetProjectDir,
12
+ copyPreCommitHook,
13
+ setupLibraryGitHooks,
14
+ installHooks,
15
+ getHooksDirFromEnv,
16
+ HOOKS_DIR
17
+ } = require('../src/install-hooks.js');
18
+
19
+ describe('install-hooks', () => {
20
+ let consoleSpy, warnSpy;
21
+ const originalEnv = process.env;
22
+
23
+ beforeEach(() => {
24
+ // Reset all mocks
25
+ jest.clearAllMocks();
26
+
27
+ // Setup fs mocks explicitly
28
+ fs.existsSync = jest.fn();
29
+ fs.mkdirSync = jest.fn();
30
+ fs.copyFileSync = jest.fn();
31
+ fs.chmodSync = jest.fn();
32
+ fs.readFileSync = jest.fn();
33
+
34
+ // Setup child_process mocks explicitly
35
+ execSync.mockReset();
36
+
37
+ // Reset environment
38
+ process.env = { ...originalEnv };
39
+ delete process.env.INIT_CWD;
40
+
41
+ // Setup console spies
42
+ consoleSpy = jest.spyOn(console, 'log').mockImplementation();
43
+ warnSpy = jest.spyOn(console, 'warn').mockImplementation();
44
+ });
45
+
46
+ afterEach(() => {
47
+ // Restore environment and spies
48
+ process.env = originalEnv;
49
+ consoleSpy.mockRestore();
50
+ warnSpy.mockRestore();
51
+ });
52
+
53
+ describe('getHooksDirFromEnv', () => {
54
+ beforeEach(() => {
55
+ // Reset process.cwd for these tests
56
+ jest.spyOn(process, 'cwd').mockReturnValue('/test/project');
57
+ });
58
+
59
+ afterEach(() => {
60
+ process.cwd.mockRestore();
61
+ });
62
+
63
+ it('should return default value when .env file does not exist', () => {
64
+ fs.existsSync.mockReturnValue(false);
65
+
66
+ const result = getHooksDirFromEnv();
67
+
68
+ expect(result).toBe('.git-hooks');
69
+ expect(fs.existsSync).toHaveBeenCalledWith('/test/project/.env');
70
+ });
71
+
72
+ it('should return value from .env file when HOOKS_DIR is set', () => {
73
+ fs.existsSync.mockReturnValue(true);
74
+ fs.readFileSync.mockReturnValue('NODE_ENV=test\nHOOKS_DIR=custom-hooks\nOTHER=value');
75
+
76
+ const result = getHooksDirFromEnv();
77
+
78
+ expect(result).toBe('custom-hooks');
79
+ expect(fs.readFileSync).toHaveBeenCalledWith('/test/project/.env', 'utf8');
80
+ });
81
+
82
+ it('should return default value when .env exists but HOOKS_DIR is not set', () => {
83
+ fs.existsSync.mockReturnValue(true);
84
+ fs.readFileSync.mockReturnValue('NODE_ENV=test\nOTHER=value');
85
+
86
+ const result = getHooksDirFromEnv();
87
+
88
+ expect(result).toBe('.git-hooks');
89
+ });
90
+
91
+ it('should remove quotes from HOOKS_DIR value', () => {
92
+ fs.existsSync.mockReturnValue(true);
93
+ fs.readFileSync.mockReturnValue('HOOKS_DIR="quoted-hooks"');
94
+
95
+ const result = getHooksDirFromEnv();
96
+
97
+ expect(result).toBe('quoted-hooks');
98
+ });
99
+
100
+ it('should remove single quotes from HOOKS_DIR value', () => {
101
+ fs.existsSync.mockReturnValue(true);
102
+ fs.readFileSync.mockReturnValue("HOOKS_DIR='single-quoted'");
103
+
104
+ const result = getHooksDirFromEnv();
105
+
106
+ expect(result).toBe('single-quoted');
107
+ });
108
+
109
+ it('should handle file read errors gracefully', () => {
110
+ fs.existsSync.mockReturnValue(true);
111
+ fs.readFileSync.mockImplementation(() => {
112
+ throw new Error('Permission denied');
113
+ });
114
+
115
+ const result = getHooksDirFromEnv();
116
+
117
+ expect(result).toBe('.git-hooks');
118
+ });
119
+
120
+ it('should return default value when HOOKS_DIR is empty', () => {
121
+ fs.existsSync.mockReturnValue(true);
122
+ fs.readFileSync.mockReturnValue('HOOKS_DIR=\nOTHER=value');
123
+
124
+ const result = getHooksDirFromEnv();
125
+
126
+ expect(result).toBe('.git-hooks');
127
+ });
128
+ });
129
+
130
+ describe('isExternalInstallation', () => {
131
+ it('should return false when INIT_CWD is not set', () => {
132
+ expect(isExternalInstallation()).toBe(false);
133
+ });
134
+
135
+ it('should return false when INIT_CWD equals current working directory', () => {
136
+ process.env.INIT_CWD = process.cwd();
137
+ expect(isExternalInstallation()).toBe(false);
138
+ });
139
+
140
+ it('should return true when INIT_CWD differs from current working directory', () => {
141
+ process.env.INIT_CWD = '/different/path';
142
+ expect(isExternalInstallation()).toBe(true);
143
+ });
144
+ });
145
+
146
+ describe('getTargetProjectDir', () => {
147
+ it('should return INIT_CWD when set', () => {
148
+ const targetDir = '/target/project';
149
+ process.env.INIT_CWD = targetDir;
150
+ expect(getTargetProjectDir()).toBe(targetDir);
151
+ });
152
+
153
+ it('should return current working directory when INIT_CWD is not set', () => {
154
+ expect(getTargetProjectDir()).toBe(process.cwd());
155
+ });
156
+ });
157
+
158
+ describe('copyPreCommitHook', () => {
159
+ const targetDir = '/target/project';
160
+ const targetHooksDir = path.join(targetDir, HOOKS_DIR);
161
+ const targetPreCommit = path.join(targetHooksDir, 'pre-commit');
162
+
163
+ beforeEach(() => {
164
+ process.env.INIT_CWD = targetDir;
165
+ });
166
+
167
+ it('should skip copy if pre-commit already exists', () => {
168
+ fs.existsSync.mockReturnValue(true);
169
+
170
+ copyPreCommitHook();
171
+
172
+ expect(fs.copyFileSync).not.toHaveBeenCalled();
173
+ expect(consoleSpy).toHaveBeenCalledWith(
174
+ `✅ ${HOOKS_DIR}/pre-commit already exists, skipping copy`
175
+ );
176
+ });
177
+
178
+ it('should warn if source pre-commit does not exist', () => {
179
+ fs.existsSync.mockImplementation((filePath) => {
180
+ if (filePath === targetPreCommit) return false;
181
+ if (filePath.includes('.git-hooks/pre-commit-sample')) return false;
182
+ return true;
183
+ });
184
+
185
+ copyPreCommitHook();
186
+
187
+ expect(fs.copyFileSync).not.toHaveBeenCalled();
188
+ expect(warnSpy).toHaveBeenCalledWith('⚠️ Source pre-commit hook not found, skipping copy');
189
+ });
190
+
191
+ it('should create directory and copy file when conditions are met', () => {
192
+ // Mock file existence checks
193
+ fs.existsSync.mockImplementation((filePath) => {
194
+ if (filePath === targetPreCommit) return false; // Target doesn't exist
195
+ if (filePath === targetHooksDir) return false; // Directory doesn't exist
196
+ if (filePath.includes('.git-hooks/pre-commit-sample')) return true; // Source exists
197
+ return false;
198
+ });
199
+
200
+ copyPreCommitHook();
201
+
202
+ expect(fs.mkdirSync).toHaveBeenCalledWith(targetHooksDir, { recursive: true });
203
+ expect(fs.copyFileSync).toHaveBeenCalled();
204
+ expect(fs.chmodSync).toHaveBeenCalledWith(targetPreCommit, 0o755);
205
+ expect(consoleSpy).toHaveBeenCalledWith(`📁 Created ${HOOKS_DIR} directory`);
206
+ expect(consoleSpy).toHaveBeenCalledWith(
207
+ `✅ Copied pre-commit hook to ${HOOKS_DIR}/pre-commit`
208
+ );
209
+ expect(consoleSpy).toHaveBeenCalledWith(
210
+ `💡 To use it, run: git config core.hooksPath ${HOOKS_DIR}`
211
+ );
212
+ });
213
+
214
+ it('should handle copy errors gracefully', () => {
215
+ fs.existsSync.mockImplementation((filePath) => {
216
+ if (filePath === targetPreCommit) return false;
217
+ if (filePath.includes('.git-hooks/pre-commit-sample')) return true;
218
+ return false;
219
+ });
220
+ fs.copyFileSync.mockImplementation(() => {
221
+ throw new Error('Permission denied');
222
+ });
223
+
224
+ copyPreCommitHook();
225
+
226
+ expect(warnSpy).toHaveBeenCalledWith(
227
+ '⚠️ Failed to copy pre-commit hook:',
228
+ 'Permission denied'
229
+ );
230
+ });
231
+ });
232
+
233
+ describe('setupLibraryGitHooks', () => {
234
+ it('should configure git hooks successfully', () => {
235
+ execSync.mockImplementation(() => {});
236
+
237
+ setupLibraryGitHooks();
238
+
239
+ expect(execSync).toHaveBeenCalledWith(`git config core.hooksPath ${HOOKS_DIR}`, {
240
+ stdio: 'ignore'
241
+ });
242
+ expect(execSync).toHaveBeenCalledWith(`chmod +x ./${HOOKS_DIR}/*`, { stdio: 'ignore' });
243
+ expect(consoleSpy).toHaveBeenCalledWith(`✅ Configured git to use ${HOOKS_DIR} directory`);
244
+ expect(consoleSpy).toHaveBeenCalledWith('✅ Made git hooks executable');
245
+ });
246
+
247
+ it('should handle git configuration errors gracefully', () => {
248
+ execSync.mockImplementation((cmd) => {
249
+ if (cmd.includes('git config')) {
250
+ throw new Error('Not a git repository');
251
+ }
252
+ });
253
+
254
+ setupLibraryGitHooks();
255
+
256
+ expect(warnSpy).toHaveBeenCalledWith(
257
+ '⚠️ Could not setup library git hooks:',
258
+ 'Not a git repository'
259
+ );
260
+ });
261
+ });
262
+
263
+ describe('installHooks', () => {
264
+ it('should setup library hooks and skip copy in development mode', () => {
265
+ process.env.INIT_CWD = process.cwd(); // Same as current directory
266
+
267
+ // Mock successful library setup
268
+ execSync.mockImplementation(() => {});
269
+
270
+ installHooks();
271
+
272
+ expect(consoleSpy).toHaveBeenCalledWith('🔧 Setting up prettier-staged hooks...');
273
+ expect(consoleSpy).toHaveBeenCalledWith('🏠 Running in development mode, skipping hook copy');
274
+ expect(consoleSpy).toHaveBeenCalledWith('✨ Setup complete!');
275
+ });
276
+
277
+ it('should setup library hooks and copy to target project in external installation', () => {
278
+ process.env.INIT_CWD = '/different/project';
279
+
280
+ // Mock successful operations
281
+ execSync.mockImplementation(() => {});
282
+ fs.existsSync.mockImplementation((filePath) => {
283
+ return !!filePath.includes('.git-hooks/pre-commit-sample'); // Target doesn't exist
284
+ });
285
+
286
+ installHooks();
287
+
288
+ expect(consoleSpy).toHaveBeenCalledWith('🔧 Setting up prettier-staged hooks...');
289
+ expect(consoleSpy).toHaveBeenCalledWith(
290
+ '📦 Installing as dependency, copying pre-commit hook...'
291
+ );
292
+ expect(consoleSpy).toHaveBeenCalledWith('✨ Setup complete!');
293
+ });
294
+ });
295
+ });